HTTP 扩展

概述

目前 go-zero 提供非常强大的 http能力,但有部分功能仍未在 go-zero 实现,在 zeromicro 下有一个 x 仓库专门用于对 go-zero 的扩展,其中 HTTP 的扩展支持了:

  1. code-data 响应格式支持
  2. xml 响应支持
  3. code-msg error 类型支持

详情可参考 https://github.com/zeromicro/x

code-data 统一响应格式用法

在很多情况下,为了和前端达成统一的响应格式,我们通常会封装一层业务 code,msg 及业务数据,常见格式如下:

  1. {
  2. "code": 0,
  3. "msg": "ok",
  4. "data": {
  5. ...
  6. }
  7. }

目前如果需要实现这种格式响应,有2种做法:

  1. 自定义响应格式
  2. 使用 go-zero 扩展包来实现

下文我们以 go-zero 的扩展包来演示一下。

  1. 初始化一个 demo 工程

    1. $ mkdir demo && cd demo
    2. $ go mod init demo
  2. 在 demo 目录下创建一个 api 文件 user.api,添加如下内容

    1. syntax = "v1"
    2. type LoginRequest {
    3. Username string `json:"username"`
    4. Password string `json:"password"`
    5. }
    6. type LoginResponse {
    7. UID int64 `json:"uid"`
    8. Name string `json:"name"`
    9. }
    10. service user {
    11. @handler login
    12. post /user/login (LoginRequest) returns (LoginResponse)
    13. }
  3. 通过 goctl 生成代码

    1. $ goctl api go --api user.api --dir .
    2. Done.
  4. 添加 mock 逻辑,修改 demo/internal/logic/loginlogic.go 文件,使其代码变为:

    1. package logic
    2. import (
    3. "context"
    4. "demo/internal/svc"
    5. "demo/internal/types"
    6. "github.com/zeromicro/go-zero/core/logx"
    7. "github.com/zeromicro/x/errors"
    8. )
    9. type LoginLogic struct {
    10. logx.Logger
    11. ctx context.Context
    12. svcCtx *svc.ServiceContext
    13. }
    14. func NewLoginLogic(ctx context.Context, svcCtx *svc.ServiceContext) *LoginLogic {
    15. return &LoginLogic{
    16. Logger: logx.WithContext(ctx),
    17. ctx: ctx,
    18. svcCtx: svcCtx,
    19. }
    20. }
    21. func (l *LoginLogic) Login(req *types.LoginRequest) (resp *types.LoginResponse, err error) {
    22. // 模拟登录逻辑
    23. if req.Username != "go-zero" || req.Password != "123456" {
    24. return nil, errors.New(1001, "用户名或密码错误")
    25. }
    26. resp = new(types.LoginResponse)
    27. resp.Name = "go-zero"
    28. resp.UID = 1
    29. return resp, nil
    30. }

    至此,我们先来看一下没有修改 demo/internal/handler/loginhandler.go 文件前的响应格式:

    1. $ cd demo
    2. $ go mod tidy
    3. $ go run user.go
    4. # 正常业务逻辑响应格式演示
    5. curl --location '127.0.0.1:8888/user/login' \
    6. --header 'Content-Type: application/json' \
    7. --data '{
    8. "username":"go-zero",
    9. "password":"123456"
    10. }'
    11. {"uid":1,"name":"go-zero"}
    12. # 错误逻辑响应格式演示
    13. curl --location '127.0.0.1:8888/user/login' \
    14. --header 'Content-Type: application/json' \
    15. --data '{
    16. "username":"go-zero",
    17. "password":"111111"
    18. }'
    19. code: 1001, msg: 用户名或密码错误
  5. 接着上面步骤,我们修改一下 demo/internal/handler/loginhandler.go 文件,将 loginHandler 中的响应方法替换成扩展包中的方法

HTTP 扩展 - 图1温馨提示

这一步,为了防止每次代码生成都要更改,可以通过修改模板来实现,自定义模板用法可参考 《模板定制化》

  1. package handler
  2. import (
  3. "net/http"
  4. "demo/internal/logic"
  5. "demo/internal/svc"
  6. "demo/internal/types"
  7. "github.com/zeromicro/go-zero/rest/httpx"
  8. xhttp "github.com/zeromicro/x/http"
  9. )
  10. func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  11. return func(w http.ResponseWriter, r *http.Request) {
  12. var req types.LoginRequest
  13. if err := httpx.Parse(r, &req); err != nil {
  14. xhttp.JsonBaseResponseCtx(r.Context(), w, err)
  15. return
  16. }
  17. l := logic.NewLoginLogic(r.Context(), svcCtx)
  18. resp, err := l.Login(&req)
  19. if err != nil {
  20. // code-data 响应格式
  21. xhttp.JsonBaseResponseCtx(r.Context(), w, err)
  22. } else {
  23. // code-data 响应格式
  24. xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
  25. }
  26. }
  27. }

重新运行 user 程序,再来请求查看一下响应格式:

  1. $ go run user.go
  2. # 正常业务逻辑响应格式演示
  3. curl --location '127.0.0.1:8888/user/login' \
  4. --header 'Content-Type: application/json' \
  5. --data '{
  6. "username":"go-zero",
  7. "password":"123456"
  8. }'
  9. {"code":0,"msg":"ok","data":{"uid":1,"name":"go-zero"}}
  10. # 错误逻辑响应格式演示
  11. curl --location '127.0.0.1:8888/user/login' \
  12. --header 'Content-Type: application/json' \
  13. --data '{
  14. "username":"go-zero",
  15. "password":"111111"
  16. }'
  17. {"code":1001,"msg":"用户名或密码错误"}

xml 响应格式支持

继上文,我们将 demo/internal/handler/loginhandler.go 文件中的响应改成 xhttp.OkXml 或者 OkXmlCtx 即可,如果需要使用同一个的 code-data 响应格式,可替换成 xhttp.XmlBaseResponse 或者 xhttp.XmlBaseResponseCtx 格式,我们以 xhttp.XmlBaseResponseCtx 为例子,修改后代码如下:

  1. package handler
  2. import (
  3. "net/http"
  4. "demo/internal/logic"
  5. "demo/internal/svc"
  6. "demo/internal/types"
  7. "github.com/zeromicro/go-zero/rest/httpx"
  8. xhttp "github.com/zeromicro/x/http"
  9. )
  10. func loginHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
  11. return func(w http.ResponseWriter, r *http.Request) {
  12. var req types.LoginRequest
  13. if err := httpx.Parse(r, &req); err != nil {
  14. xhttp.XmlBaseResponseCtx(r.Context(), w, err)
  15. return
  16. }
  17. l := logic.NewLoginLogic(r.Context(), svcCtx)
  18. resp, err := l.Login(&req)
  19. if err != nil {
  20. //xhttp.XmlBaseResponse(w,err)
  21. xhttp.XmlBaseResponseCtx(r.Context(),w,err)
  22. } else {
  23. //xhttp.XmlBaseResponse(w,resp)
  24. xhttp.XmlBaseResponseCtx(r.Context(),w,resp)
  25. }
  26. }
  27. }

我们重新运行 user 程序,查看一下响应格式:

  1. $ go run user.go
  2. # 正常业务逻辑响应格式演示
  3. curl --location '127.0.0.1:8888/user/login' \
  4. --header 'Content-Type: application/json' \
  5. --data '{
  6. "username":"go-zero",
  7. "password":"123456"
  8. }'
  9. <xml version="1.0" encoding="UTF-8"><code>0</code><msg>ok</msg><data><UID>1</UID><Name>go-zero</Name></data></xml>
  10. # 错误逻辑响应格式演示
  11. curl --location '127.0.0.1:8888/user/login' \
  12. --header 'Content-Type: application/json' \
  13. --data '{
  14. "username":"go-zero",
  15. "password":"111111"
  16. }'
  17. <xml version="1.0" encoding="UTF-8"><code>1001</code><msg>用户名或密码错误</msg></xml>

参考文献