gozero基础-4.api服务

api语法

api文件就是对这个服务所有api的描述

服务名,函数名,路径,请求方法,请求参数,响应参数

我们以用户管理的两个重要接口为例,去编写它的api文件

type LoginRequest {
  UserName string `json:"userName"`
  Password string `json:"password"`
}

type Response {
  Code int    `json:"code"`
  Data string `json:"data"`
  Msg  string `json:"msg"`
}

type UserInfo {
  UserName string `json:"userName"`
  Addr     string `json:"addr"`
  Id       uint   `json:"id"`
}

type UserInfoResponse {
  Code int      `json:"code"`
  Data UserInfo `json:"data"`
  Msg  string   `json:"msg"`
}

service users {
  @handler login
  post /api/users/login (LoginRequest) returns (Response)
  
  @handler userInfo
  get /api/users/info returns (UserInfoResponse)
}

// goctl api go -api v1.api -dir .

通过这个示例,我们发现实际操作起来还是有些问题

  1. 响应如何封装?
  2. 统一api前缀
  3. 用户信息接口应该要进行jwt验证
  4. api文档

响应封装

不把code,data,msg写在api里面,我们通过封装统一响应

在统一响应里面去加上code data msg

type LoginRequest {
  UserName string `json:"userName"`
  Password string `json:"password"`
}

type UserInfoResponse {
  UserName string `json:"userName"`
  Addr     string `json:"addr"`
  Id       uint   `json:"id"`
}

service users {
  @handler login
  post /login (LoginRequest) returns (string)
  
  @handler userInfo
  get /info returns (UserInfoResponse)
}

// goctl api go -api v1.api -dir .

在common/response/enter.go中

package response

import (
  "github.com/zeromicro/go-zero/rest/httpx"
  "net/http"
)

type Body struct {
  Code uint32      `json:"code"`
  Msg  string      `json:"msg"`
  Data interface{} `json:"data"`
}

// Response http返回
func Response(r *http.Request, w http.ResponseWriter, resp interface{}, err error) {
  if err == nil {
    //成功返回
    r := &Body{
      Code: 0,
      Msg:  "成功",
      Data: resp,
    }
    httpx.WriteJson(w, http.StatusOK, r)
    return
  }
  //错误返回
  errCode := uint32(10086)
  // 可以根据错误码,返回具体错误信息
  errMsg := "服务器错误"

  httpx.WriteJson(w, http.StatusBadRequest, &Body{
    Code: errCode,
    Msg:  errMsg,
    Data: nil,
  })

}

修改一下handler的响应逻辑

l := logic.NewLoginLogic(r.Context(), svcCtx)
resp, err := l.Login(&req)
response.Response(r, w, resp, err)

然后完善逻辑即可

func (l *LoginLogic) Login(req *types.LoginRequest) (resp string, err error) {
  // todo: add your logic here and delete this line
  fmt.Println(req.UserName, req.Password)
  return "xxxx.xxxx.xxx", nil
}

模板定制化

当然官方提供了修改模板的方式,避免每次生成都要去改

模板定制化 | go-zero Documentation

先全局搜一下 handler.tpl这个文件

如果没有就先用这个命令生成

goctl template init

修改为:

package handler

import (
    "net/http"
    "github.com/zeromicro/go-zero/rest/httpx"
    "go_test/common/response"
    {{.ImportPackages}}
)

func {{.HandlerName}}(svcCtx *svc.ServiceContext) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        {{if .HasRequest}}var req types.{{.RequestType}}
        if err := httpx.Parse(r, &req); err != nil {
            httpx.Error(w, err)
            return
        }{{end}}

        l := logic.New{{.LogicType}}(r.Context(), svcCtx)
        {{if .HasResp}}resp, {{end}}err := l.{{.Call}}({{if .HasRequest}}&req{{end}})
        {{if .HasResp}}response.Response(r, w, resp, err){{else}}response.Response(r, w, nil, err){{end}}

    }
}

api前缀

对于用户服务而言,api的前缀都是 /api/users

@server (
    prefix: /api/users
)
service users {
    @handler login
    post /login (LoginRequest) returns (string)

    @handler userInfo
    get /info returns (UserInfoResponse)
}

jwt及验证

type LoginRequest {
  UserName string `json:"userName"`
  Password string `json:"password"`
}

type UserInfoResponse {
  UserName string `json:"userName"`
  Addr     string `json:"addr"`
  Id       uint   `json:"id"`
}

@server(
  prefix: /api/users
)
service users {
  @handler login
  post /login (LoginRequest) returns (string)
}


@server(
  jwt: Auth
  prefix: /api/users
)
service users {
  @handler userInfo
  get /info returns (UserInfoResponse)
}

转换之后,修改配置文件

AccessExpire的单位是秒

Name: users
Host: 0.0.0.0
Port: 8888
Auth:
  AccessSecret: duerueudfnd235sdh
  AccessExpire: 3600

jwt公共代码

package jwts

import (
  "errors"
  "github.com/golang-jwt/jwt/v4"
  "time"
)

// JwtPayLoad jwt中payload数据
type JwtPayLoad struct {
  UserID   uint   `json:"user_id"`
  Username string `json:"username"` // 用户名
  Role     int    `json:"role"`     // 权限  1 普通用户  2 管理员
}

type CustomClaims struct {
  JwtPayLoad
  jwt.RegisteredClaims
}

// GenToken 创建 Token
func GenToken(user JwtPayLoad, accessSecret string, expires int64) (string, error) {
  claim := CustomClaims{
    JwtPayLoad: user,
    RegisteredClaims: jwt.RegisteredClaims{
      ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Hour * time.Duration(expires))),
    },
  }

  token := jwt.NewWithClaims(jwt.SigningMethodHS256, claim)
  return token.SignedString([]byte(accessSecret))
}

// ParseToken 解析 token
func ParseToken(tokenStr string, accessSecret string, expires int64) (*CustomClaims, error) {

  token, err := jwt.ParseWithClaims(tokenStr, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
    return []byte(accessSecret), nil
  })
  if err != nil {
    return nil, err
  }
  if claims, ok := token.Claims.(*CustomClaims); ok && token.Valid {
    return claims, nil
  }
  return nil, errors.New("invalid token")
}


在登录成功之后签发jwt

loginlogic.go


func (l *LoginLogic) Login(req *types.LoginRequest) (resp string, err error) {
  // todo: add your logic here and delete this line
  auth := l.svcCtx.Config.Auth
  token, err := jwts.GenToken(jwts.JwtPayLoad{
    UserID:   1,
    Username: "枫枫",
    Role:     1,
  }, auth.AccessSecret, auth.AccessExpire)
  if err != nil {
    return "", err
  }
  return token, err
}


然后在userinfologic里面加上必要的逻辑

func (l *UserInfoLogic) UserInfo() (resp *types.UserInfoResponse, err error) {
  // todo: add your logic here and delete this line

  userId := l.ctx.Value("user_id").(json.Number)
  fmt.Printf("%v, %T, \n", userId, userId)
  username := l.ctx.Value("username").(string)
  uid, _ := userId.Int64()

  return &types.UserInfoResponse{
    UserId:   uint(uid),
    Username: username,
  }, nil
}


userinfo这个接口就已经自动加上jwt的验证了

不过这个token是需要这样加

headers:{
  Authorization: "Bearer token"
}

没有通过jwt的响应是401,这个需要留意一下

当然,也能修改jwt验证的响应

在main中,加上jwt验证的回调函数即可

func main() {
  flag.Parse()

  var c config.Config
  conf.MustLoad(*configFile, &c)

  server := rest.MustNewServer(c.RestConf, rest.WithUnauthorizedCallback(JwtUnauthorizedResult))
  defer server.Stop()

  ctx := svc.NewServiceContext(c)
  handler.RegisterHandlers(server, ctx)

  fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
  server.Start()
}

// JwtUnauthorizedResult jwt验证失败的回调
func JwtUnauthorizedResult(w http.ResponseWriter, r *http.Request, err error) {
  fmt.Println(err) // 具体的错误,没带token,token过期?伪造token?
  httpx.WriteJson(w, http.StatusOK, response.Body{10087, "鉴权失败", nil})
}

生成api文档

后端对外的api,肯定要和前端进行对接

那么在go-zero里面怎么生成api接口文档呢

  1. 安装goctl-swagger
go install github.com/zeromicro/goctl-swagger@latest
  1. 生成app.json

如果没有doc目录,需要创建

goctl api plugin -plugin goctl-swagger="swagger -filename app.json -host localhost:8888 -basepath /" -api v1.api -dir ./doc
  1. 使用docker,查看这个swagger页面
docker run -d --name swag -p 8087:8080 -e SWAGGER_JSON=/opt/app.json -v D:\IT\go_project3\go_test\v1\api\doc\:/opt swaggerapi/swagger-ui

可以再完善下api信息

@server(
  prefix: /api/users
)
service users {
  @doc(
    summary: "用户登录"
  )
  @handler login
  post /login (LoginRequest) returns (string)
}

@server(
  jwt: Auth
  prefix: /api/users
)
service users {
  @doc(
    summary: "获取用户信息"
  )
  @handler userInfo
  get /info returns (UserInfoResponse)
}

改为再重新生成一下 json

但是,我发现这个swagger体验不怎么好,使用了自定义响应之后,swag这里改不了

公司项目的话,都是有自己的api平台

团队项目的话,也可以用apifox

所以,个人用swagger的话,凑活着用也不是不行

参考文档

api语法 go-zero 基础 -- 框架设计_go-zero doc-CSDN博客

官方api规范 API 规范 | go-zero Documentation

go-zero使用jwt go-zero学习 — 进阶_go-zero nacos-CSDN博客

自定义jwt错误 go-zero鉴权(jwt)失败回调函数错误处理-CSDN博客

goctl-swagger的坑 go-zero插件goctl-swagger的坑 - 丶吃鱼的猫 - 博客园

goctl-swagger使用 goctl-swagger的使用方法-CSDN博客

### 回答1: GoZero是一个基于Golang的Web框架,提供了丰富的API开发功能。下面是GoZero API撰写规范的一些建议: 1. API路由规范 路由应该简洁明了,易于理解。建议采用RESTful API风格,尽量使用简短的英文单词和短横线分隔符,例如: ``` // 获取用户信息 GET /users/:id // 创建新用户 POST /users // 更新用户信息 PUT /users/:id // 删除用户 DELETE /users/:id ``` 2. API参数规范 API参数应该有明确的名称和类型,并且应该进行验证。建议使用以下规则: - 参数名称应该清晰明了 - 参数类型应该尽量使用Golang内置类型 - 参数应该进行验证,确保它们符合预期 3. API响应规范 API的响应应该是具有可读性的,并且应该包含足够的信息以便客户端正确处理它。建议使用以下规则: - 响应应该包含HTTP状态码,例如200,404或500 - 响应应该包含可读性强的消息 - 响应应该包含数据,例如JSON或XML格式的数据 4. API错误处理规范 API应该能够处理错误,并返回有意义的错误消息。建议使用以下规则: - 错误信息应该是可读性强的 - 错误应该包含HTTP状态码,例如404或500 - 错误应该包含错误原因和解决方案 5. API安全规范 API应该采取适当的安全措施,以确保API和客户端之间的通信是安全的。建议使用以下规则: - 所有API请求都应该使用HTTPS - 所有API请求都应该进行身份验证,例如基于令牌的身份验证 - 所有API请求都应该进行授权,以确保只有授权用户才能访问API 以上是GoZero API撰写规范的一些建议,可以根据自己的实际情况进行调整。 ### 回答2: GoZero API撰写规范是一套规范,用于指导开发者在使用GoZero框架编写API时,如何进行统一的命名、组织和编写规范,以及如何提高代码的可读性和可维护性。 首先,GoZero API撰写规范要求开发者使用有意义和描述性的命名方式来命名API接口、请求和响应结构体等,以便于其他开发者理解和维护代码。这包括使用驼峰命名法、避免使用缩写和过于简短的命名等。 其次,规范要求开发者按照功能和模块来组织API,将相关的接口放在同一个文件或同一个文件夹中,方便查找和管理。同时,要求在接口的注释中清晰地说明接口的功能、请求参数、响应格式及状态码等,方便其他开发者调用和理解。 此外,规范要求开发者在编写API时,遵循RESTful风格的设计原则,即使用合适的HTTP方法(GET、POST、PUT、DELETE等)和URL路径,将数据的增删改查操作与HTTP方法对应,提高API的可读性和易用性。 另外,规范还鼓励开发者使用API版本控制,通过在URL中添加版本号的方式,来区分不同版本的API接口,以便于后续的修改和升级。 最后,规范要求开发者在错误处理和异常处理方面,使用合适的状态码和错误信息,以便于客户端能够正确处理和响应错误,并提供良好的用户体验。 总之,GoZero API撰写规范是为了提高代码的可读性、可维护性和可扩展性而制定的一套规范,通过统一的命名、组织和编写规范,帮助开发者编写高质量的API接口。 ### 回答3: go-zero 是一个针对 Go 语言开发的微服务框架,它提供了 goctl 工具来帮助开发者生成相应的代码和文件。go-zero api 撰写规范指的是在编写 go-zero 的接口定义时要遵守的一些规范。 首先,接口的路径应该使用小写字母加下划线的命名方式,例如:/user/login。 其次,接口的方法应该使用大写字母开头的驼峰命名方式,例如:GetUserById。 接口定义时应该严格按照 RESTful 风格,即使用 GET、POST、PUT、DELETE 等 HTTP 请求方法来对资源进行操作。遵循这种风格可以使接口整体结构更加清晰和易于理解。 接口的参数应该使用结构体的方式定义,结构体的字段名称应该使用大写字母开头的驼峰命名方式,并且可以使用标签来指定字段在接口请求时的名称。 接口的返回值也应该使用结构体的方式定义,结构体的字段名称同样应该使用大写字母开头的驼峰命名方式。 在接口的注释中,应该清晰地描述接口的功能、参数的含义以及返回结果的格式,方便其他开发者理解和使用接口。 最后,在编写完接口定义后,使用 goctl 工具生成相应的代码和文件,这样可以避免手写代码可能出现的错误,提高开发效率。 总的来说,go-zero api 撰写规范主要包括路径、方法、参数、返回值的命名规范,遵循 RESTful 风格,以及写清楚注释并使用 goctl 工具生成代码和文件。这些规范可以使接口的调用更加一致和规范,提高代码的可读性和可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值