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:
syntax = "v1"
type UserInfoRequest {
Id int64 `path:"id"`
}
type UserInfoResponse {
Id int64 `json:"id"`
Name string `json:"name"`
Age int32 `json:"age"`
}
@server(
// Middleware is declared through the middleware keyword, and multiple middleware are separated by English commas, such asUserAgentMiddleware,LogMiddleware
middleware: UserAgentMiddleware
)
service user {
@handler userinfo
get /user/info/:id (UserInfoRequest) returns (UserInfoResponse)
}
In the example above, we have stated a midpoint UserAgentMidleare
and then declare the middle of a keyword in @server
by middileware
Let’s look at the generated intermediate code:
Directory Structure
.
├── etc
│ └── user.yaml
├── internal
│ ├── config
│ │ └── config.go
│ ├── handler
│ │ ├── routes.go
│ │ └── userinfohandler.go
│ ├── logic
│ │ └── userinfologic.go
│ ├── middleware # middleware directory
│ │ └── useragentmiddleware.go
│ ├── svc
│ │ └── servicecontext.go
│ └── types
│ └── types.go
├── user.api
└── user.go
8 directories, 10 files
Middleware code (no fill logic)
- useragentmiddleware.go
- servicecontext.go
- routes.go
package middleware
import "net/http"
type UserAgentMiddleware struct {
}
func NewUserAgentMiddleware() *UserAgentMiddleware {
return &UserAgentMiddleware{}
}
func (m *UserAgentMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// TODO generate middleware implement function, delete after code implementation
// Passthrough to next handler if need
next(w, r)
}
}
package svc
import (
"demo/user/internal/config"
"demo/user/internal/middleware"
"github.com/zeromicro/go-zero/rest"
)
type ServiceContext struct {
Config config.Config
UserAgentMiddleware rest.Middleware
}
func NewServiceContext(c config.Config) *ServiceContext {
return &ServiceContext{
Config: c,
UserAgentMiddleware: middleware.NewUserAgentMiddleware().Handle,
}
}
// Code generated by goctl. DO NOT EDIT.
package handler
import (
"net/http"
"demo/user/internal/svc"
"github.com/zeromicro/go-zero/rest"
)
func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
server.AddRoutes(
rest.WithMiddlewares(
[]rest.Middleware{serverCtx.UserAgentMiddleware},
[]rest.Route{
{
Method: http.MethodGet,
Path: "/user/info/:id",
Handler: userinfoHandler(serverCtx),
},
}...,
),
)
}
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:
package middleware
import (
"context"
"net/http"
)
type UserAgentMiddleware struct {
}
func NewUserAgentMiddleware() *UserAgentMiddleware {
return &UserAgentMiddleware{}
}
func (m *UserAgentMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
val := r.Header.Get("User-Agent")
reqCtx := r.Context()
ctx := context.WithValue(reqCtx, "User-Agent", val)
newReq := r.WithContext(ctx)
next(w, newReq)
}
}