go-zero 实战 - User Userinfo

511 阅读3分钟

我们通过一个系列文章跟大家详细展示一个 go-zero 微服务实例,整个系列分十三篇文章,目录结构如下:

  1. go-zero 实战 - 服务划分与项目创建
  2. go-zero 实战 - User API Gateway
  3. go-zero 实战 - User Login
  4. go-zero 实战 - User Register
  5. go-zero 实战 - User Userinfo
  6. go-zero 实战 - Food API Gateway
  7. go-zero 实战 - Food Search
  8. go-zero 实战 - Food AddFood
  9. go-zero 实战 - Food DeleteFood
  10. go-zero 实战 - Food Foodlist
  11. go-zero 实战进阶 - rpc 服务
  12. go-zero 实战进阶 - 用户管理 rpc 服务
  13. go-zero 实战进阶 - 食材管理 rpc 服务

期望通过本系列文章带你在本地利用 go-zero 快速开发一个《食谱指南》系统,让你快速上手微服务。

上一篇文章中,我们介绍了如何搭建注册服务,本篇我们将介绍如何搭建获取用户信息服务。

iOSAndroid 开发的小伙伴们应该都知道,userinfo 接口与 loginregister 接口不同,此类接口一般都会要求传入 userIdtoken 来请求跟该用户相关的信息,我们一般称这个过程为 鉴权,接下来的服务实现也同样设计如此。

API Gateway 增加 Jwt 鉴权

$vim usermanage/api/user.api

syntax = "v1"

info (
    title: "UserApi"
    desc: "用户服务相关 API"
    author: "DESKTOP-4T5UKHP/Owner"
    email: "renpanpan1990@163.com"
)

type (
    LoginRequest {
       Email    string `json:"email"`
       Password string `json:"password"`
    }

    LoginResponse {
       UserReply
    }
)

type (
    RegisterRequest {
       Username string `json:"username"`
       Email    string `json:"email"`
       Password string `json:"password"`
    }

    RegisterResponse {
       UserReply
    }
)

type UserInfoResponse {
    UserReply
}

type UserReply {
    Id       int64  `json:"id"`
    Username string `json:"username"`
    Email    string `json:"email"`
    JwtToken
}

type JwtToken {
    AccessToken  string `json:"accessToken,omitempty"`
    AccessExpire int64  `json:"accessExpire,omitempty"`
    RefreshAfter int64  `json:"refreshAfter,omitempty"`
}

service user-api {
    @handler Login
    post /users/login (LoginRequest) returns (LoginResponse)

    @handler Register
    post /users/register (RegisterRequest) returns(RegisterResponse)
}

@server (
    jwt: Auth
)
service user-api {
    @handler UserInfo
    post /users/userinfo returns(UserInfoResponse)
}

由于原来我们生成过 user-api 服务,userinfohandle.gouserinfologic.go 文件不符合我们现在的需求,需要删除掉这两个文件,重新生成 user-api 服务。

$ goctl api go -api user.api -dir .

我们再来查看 usermanage/api/internal/handler/routers.go 文件。 发现 users/userinfo 接口与 user/loginuser/register 接口分开处理了,并且增加了 rest.WithJwt(serverCtx.Config.Auth.AccessSecret) 鉴权处理。

// Code generated by goctl. DO NOT EDIT.
package handler

import (
    "net/http"

    "FoodGuides/service/usermanage/api/internal/svc"

    "github.com/zeromicro/go-zero/rest"
)

func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
    server.AddRoutes(
       []rest.Route{
          {
             Method:  http.MethodPost,
             Path:    "/users/login",
             Handler: LoginHandler(serverCtx),
          },
          {
             Method:  http.MethodPost,
             Path:    "/users/register",
             Handler: RegisterHandler(serverCtx),
          },
       },
    )

    server.AddRoutes(
       []rest.Route{
          {
             Method:  http.MethodPost,
             Path:    "/users/userinfo",
             Handler: UserInfoHandler(serverCtx),
          },
       },
       rest.WithJwt(serverCtx.Config.Auth.AccessSecret),
    )
}

添加用户信息逻辑

$ vim usermanage/api/internal/logic/userinfologic.go

package logic

import (
    "FoodGuides/service/usermanage/api/internal/svc"
    "FoodGuides/service/usermanage/api/internal/types"
    "FoodGuides/service/usermanage/model"
    "context"
    "encoding/json"
    "errors"

    "github.com/zeromicro/go-zero/core/logx"
)

type UserInfoLogic struct {
    logx.Logger
    ctx    context.Context
    svcCtx *svc.ServiceContext
}

func NewUserInfoLogic(ctx context.Context, svcCtx *svc.ServiceContext) *UserInfoLogic {
    return &UserInfoLogic{
       Logger: logx.WithContext(ctx),
       ctx:    ctx,
       svcCtx: svcCtx,
    }
}

func (l *UserInfoLogic) UserInfo() (*types.UserInfoResponse, error) {
    // 通过 l.ctx.Value("uid") 可获取 jwt 载体中 `uid` 信息
    uid, _ := l.ctx.Value("uid").(json.Number).Int64()
    
    // 查询用户是否存在
    user, err := l.svcCtx.UserModel.FindOne(l.ctx, uid)
    if err != nil {
       if err == model.ErrNotFound {
          return nil, errors.New("用户不存在")
       }
       return nil, err
    }

    response := types.UserReply{
       Id:       user.Id,
       Username: user.Name,
       Email:    user.Email,
    }

    return &types.UserInfoResponse{UserReply: response}, nil
}

修改 Response 返回格式

$ vim usermanage/api/internal/handler/userinfohandler.go

package handler

import (
    "FoodGuides/common/responsex"
    "net/http"

    "FoodGuides/service/usermanage/api/internal/logic"
    "FoodGuides/service/usermanage/api/internal/svc"
    "github.com/zeromicro/go-zero/rest/httpx"
)

func UserInfoHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
       l := logic.NewUserInfoLogic(r.Context(), svcCtx)
       resp, err := l.UserInfo()
       if err != nil {
          httpx.OkJson(w, responsex.FailureResponse(nil, err.Error(), 1000))
       } else {
          httpx.OkJson(w, responsex.SuccessResponse(resp, "获取成功"))
       }
    }
}

启动服务

启动 user api 服务, 运行成功后,user api 则运行在本机的 8888 端口

➜  FoodGuides:
$ go run usermanage/api/user.go -f usermanage/api/etc/user-api.yaml
Starting server at 0.0.0.0:8888...

我们用 Postman 尝试请求用户信息接口:

  1. 我们先测试不携带 Authorization 的情况。其请求结果显示未授权(401 Unauthorized),如下图所示。

image.png

  1. 我们再测试携带 Authorization 后的情况。在 PostmanAuthorization 选项中选择 Bearer Token,填写登录成功后 Api 返回的 accessToken 字段值。其请求结果如下图所示,说明用户信息服务运行正常。

image.png

注意:因为 jwt 载体中存在 uid 信息,所以只要 Authorization 不变,请求到的结果永远都是同一用户的信息。如果想请求其他用户信息,必须调用登录或注册接口,将返回的 accessToken 字段值设置到 PostmanBearer Token 中,重新发起用户信息的请求。

至此,我们就完成了有关用户三个相关接口的搭建(登录,注册,用户信息)。从下一篇开始,我们将围绕 Food 开发相关接口,并与 user 关联起来。

上一篇《go-zero 实战 - User Register》

下一篇《go-zero 实战 - Food API Gateway