Middleware declaration

Overview

In go-zero, we declared HTTP service via api language, and then generated HTTP service code via goctl, after our systematic introduction to API norm.

Middleware is a very common need in HTTP development, such as that we need to authenticate requests or log requests that are very common.

Middleware declaration

Assuming that we have a user service, we need to post user-agent information into the context message, and then process it in the logical layer according to user-agent, we can declare intermediates through api language, in api languages, we can declare intermediates by midsleware keywords and the medieval declaration format is below:

  1. syntax = "v1"
  2. type UserInfoRequest {
  3. Id int64 `path:"id"`
  4. }
  5. type UserInfoResponse {
  6. Id int64 `json:"id"`
  7. Name string `json:"name"`
  8. Age int32 `json:"age"`
  9. }
  10. @server(
  11. // Middleware is declared through the middleware keyword, and multiple middleware are separated by English commas, such asUserAgentMiddleware,LogMiddleware
  12. middleware: UserAgentMiddleware
  13. )
  14. service user {
  15. @handler userinfo
  16. get /user/info/:id (UserInfoRequest) returns (UserInfoResponse)
  17. }

In the example above, we have stated a midpoint UserAgentMidleareand then declare the middle of a keyword in @server by middileware Let’s look at the generated intermediate code:

Directory Structure

  1. .
  2. ├── etc
  3. └── user.yaml
  4. ├── internal
  5. ├── config
  6. └── config.go
  7. ├── handler
  8. ├── routes.go
  9. └── userinfohandler.go
  10. ├── logic
  11. └── userinfologic.go
  12. ├── middleware # middleware directory
  13. └── useragentmiddleware.go
  14. ├── svc
  15. └── servicecontext.go
  16. └── types
  17. └── types.go
  18. ├── user.api
  19. └── user.go
  20. 8 directories, 10 files

Middleware code (no fill logic)

  • useragentmiddleware.go
  • servicecontext.go
  • routes.go
  1. package middleware
  2. import "net/http"
  3. type UserAgentMiddleware struct {
  4. }
  5. func NewUserAgentMiddleware() *UserAgentMiddleware {
  6. return &UserAgentMiddleware{}
  7. }
  8. func (m *UserAgentMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
  9. return func(w http.ResponseWriter, r *http.Request) {
  10. // TODO generate middleware implement function, delete after code implementation
  11. // Passthrough to next handler if need
  12. next(w, r)
  13. }
  14. }
  1. package svc
  2. import (
  3. "demo/user/internal/config"
  4. "demo/user/internal/middleware"
  5. "github.com/zeromicro/go-zero/rest"
  6. )
  7. type ServiceContext struct {
  8. Config config.Config
  9. UserAgentMiddleware rest.Middleware
  10. }
  11. func NewServiceContext(c config.Config) *ServiceContext {
  12. return &ServiceContext{
  13. Config: c,
  14. UserAgentMiddleware: middleware.NewUserAgentMiddleware().Handle,
  15. }
  16. }
  1. // Code generated by goctl. DO NOT EDIT.
  2. package handler
  3. import (
  4. "net/http"
  5. "demo/user/internal/svc"
  6. "github.com/zeromicro/go-zero/rest"
  7. )
  8. func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
  9. server.AddRoutes(
  10. rest.WithMiddlewares(
  11. []rest.Middleware{serverCtx.UserAgentMiddleware},
  12. []rest.Route{
  13. {
  14. Method: http.MethodGet,
  15. Path: "/user/info/:id",
  16. Handler: userinfoHandler(serverCtx),
  17. },
  18. }...,
  19. ),
  20. )
  21. }

You can see that the middleware code is automatically generated via goctl, that the intermediate code is a structure in which there is a Handle method, which is the core method of the medium, which receives a parameter of type http.HandlerFunc and returns a parameter of type http.HandlerFunc which is used to process requests and then pass the request to the next intermediate or handler.

You can process requests in Handle methods such as authentication, log records, and then pass the request to the next intermediate or handler.

As the above requirement example, we can store the User-Agent information in the header into the context in the middleware, and the middleware is implemented as follows:

  1. package middleware
  2. import (
  3. "context"
  4. "net/http"
  5. )
  6. type UserAgentMiddleware struct {
  7. }
  8. func NewUserAgentMiddleware() *UserAgentMiddleware {
  9. return &UserAgentMiddleware{}
  10. }
  11. func (m *UserAgentMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
  12. return func(w http.ResponseWriter, r *http.Request) {
  13. val := r.Header.Get("User-Agent")
  14. reqCtx := r.Context()
  15. ctx := context.WithValue(reqCtx, "User-Agent", val)
  16. newReq := r.WithContext(ctx)
  17. next(w, newReq)
  18. }
  19. }