HTTP Extensions

Overview

Currently go-zero provides very strong http ability, but some features are still not go-zero implemented, there is a x repository dedicated to go-zero, where HTTP extensions are supported:

  1. Code-data response format support
  2. xml response support
  3. code-msg error type support

For more information, see https://github.com/zeroicro/x

code-data uniform response format usage

In many cases, in order to achieve a uniform response format with the front end, we usually encapsulate a layer of business code, msg and business data in the following common format:

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

There are currently two approaches if this format response is required:

  1. Custom response format
  2. Use go-zero extension to implement

We show below with a go-zero extension package.

  1. Initialize a demo project
  1. $ mkdir demo && cd demo
  2. $ go mod init demo
  1. Create an api file in demo directory user.api, add the following
  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. }
  1. Generate code by goctl
  1. $ goctl api go --api user.api --dir .
  2. Done.
  1. Add stock logic, modify demo/internal/logic/loginlogic.go file to make its code:
  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. // mock login
  23. if req.Username != "go-zero" || req.Password != "123456" {
  24. return nil, errors.New(1001, "invalid username or password")
  25. }
  26. resp = new(types.LoginResponse)
  27. resp.Name = "go-zero"
  28. resp.UID = 1
  29. return resp, nil
  30. }

So let’s first look at the format of the response before the file demo/internal/handler/loginhandler.go

  1. $ cd demo
  2. $ go mod tidy
  3. $ go run user.go
  4. # before
  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. # after
  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: invalid username or password
  1. Next, we modify demo/internal/handler/loginhandler.go file, replacing the response method in loginHandler with the method in the extension pack

::tip tip tips
This step can be implemented by modifying templates to prevent every time they are generated, custom templates can be referenced Template Customization :::

  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. httpx.ErrorCtx(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 response
  21. xhttp.JsonBaseResponseCtx(r.Context(), w, err)
  22. } else {
  23. // code-data response
  24. xhttp.JsonBaseResponseCtx(r.Context(), w, resp)
  25. }
  26. }
  27. }

Restart user to request a response format:

  1. $ go run user.go
  2. # no error case
  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. # error case
  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":"invalid username or password"}

xml response support

Further to the above, we will change the response from demo/internal/handler/loginhandler.go to x http.xhttp.OkXml or OkXmlCtx if the same code-data response format is required, exchange x http. mlBaseResponse or x http.XmlBaseResponseCtx For example, we revised the following code: as an example: x http.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. httpx.ErrorCtx(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. }

We rerun user program to see the response format:

  1. $ go run user.go
  2. # no error
  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. # error case
  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>invalid username or password</msg></xml>

References