当前位置:网站首页>Go deep into the underlying principles of go and rewrite redis middleware without secret
Go deep into the underlying principles of go and rewrite redis middleware without secret
2022-06-29 20:14:00 【Game programming】
###download: thorough Go Underlying principle , rewrite Redis Middleware practice
How to write Go middleware
Read request
In our example , All middleware will accept http. Handler as a parameter , And return a http.Handler. This makes it easy to string up intermediate products . The basic pattern of all our intermediate products is like this :
func X(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Something here... h.ServeHTTP(w, r) })}We want to redirect all requests to a slash —— For example /message/, Non slash equivalent to them , such as /message. We can write this way :
func TrailingSlashRedirect(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/" && r.URL.Path[len(r.URL.Path)-1] == '/' { http.Redirect(w, r, r.URL.Path[:len(r.URL.Path)-1], http.StatusMovedPermanently) return } h.ServeHTTP(w, r) })} Is it simple .
Modification request
For example , We want to add a title to the request , Or modify it .http.Handler The document indicates :
In addition to reading the body , The handler should not modify the provided request .
Go Standard library copy http.Request. The request object before passing it to the response chain , We should do the same . Suppose we want to set... On each request Request-Id head , For internal tracking . establish *Request A shallow copy of , And modify the title before the proxy .
func RequestID(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { r2 := new(http.Request) *r2 = *r r2.Header.Set("X-Request-Id", uuid.NewV4().String()) h.ServeHTTP(w, r2) })} Write response header
If you want to set the response header , You can just write them , Then the proxy requests .
func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Server", servername) h.ServeHTTP(w, r) })} The question above is , If the internal processor also sets the server header , Then your head will be covered . If you don't want to expose the server header of your internal software , Or if you want to remove the header before sending the response to the client , This can cause problems .
Do that , We must do it ourselves ResponseWriter Interface . Most of the time , We will only act as agent at the bottom ResponseWriter, But if the user tries to write a response , We'll sneak in and add our title .
type serverWriter struct { w http.ResponseWriter name string wroteHeaders bool}func (s *serverWriter) Header() http.Header { return s.w.Header()}func (s *serverWriter) WriteHeader(code int) http.Header { if s.wroteHeader == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } s.w.WriteHeader(code)}func (s *serverWriter) Write(b []byte) (int, error) { if s.wroteHeader == false { // We hit this case if user never calls WriteHeader (default 200) s.w.Header().Set("Server", s.name) s.wroteHeader = true } return s.w.Write(b)}To use it in our middleware , We will write :
func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sw := &serverWriter{ w: w, name: servername, } h.ServeHTTP(sw, r) })}If the user never calls Write or WriteHeader Well ? for example , There is one 200 State and empty body, Or a response to an option request —— None of our interceptors will run . therefore , We should ServeHTTP Add verification after calling .
func Server(h http.Handler, servername string) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { sw := &serverWriter{ w: w, name: servername, } h.ServeHTTP(sw, r) if sw.wroteHeaders == false { s.w.Header().Set("Server", s.name) s.wroteHeader = true } })} other ResponseWriter Interface
ResponseWriter The interface only needs to have three methods . But in practice , It can also respond to other interfaces , for example http.Pusher. Your middleware may be accidentally disabled HTTP/2 Support , That's not good. .
// Push implements the http.Pusher interface.func (s *serverWriter) Push(target string, opts *http.PushOptions) error { if pusher, ok := s.w.(http.Pusher); ok { return pusher.Push(target, opts) } return http.ErrNotSupported}// Flush implements the http.Flusher interface.func (s *serverWriter) Flush() { f, ok := s.w.(http.Flusher) if ok { f.Flush() }}Go To write Web middleware
middleware ( Usually ) It's a little bit of code , They receive a request , We're going to process it , Each middleware only deals with one thing , Pass it on to another middleware or final handler when it's done , In this way, the program can be decoupled . If there is no middleware, then we must complete these processing operations in the final handler , This will undoubtedly lead to the problem of bloated processing programs and low code reuse rate . Some common use cases of middleware are request logging , Header manipulation 、 HTTP Request authentication and ResponseWriter Hijacking and so on .
Middleware code template
Middleware is implemented using decorator pattern , The following middleware common code template makes it easier for us to write middleware at ordinary times , When we write middleware ourselves, we just need to fill the template with the required code logic .
func createNewMiddleware() Middleware { // Create a new middleware middleware := func(next http.HandlerFunc) http.HandlerFunc { // Create a new handler The parcel next handler := func(w http.ResponseWriter, r *http.Request) { // The processing logic of middleware ...... // Call the next middleware or the final handler The handler next(w, r) } // Return to the new package handler return handler } // Return to the new middleware return middleware} Using middleware
We create two middleware , A time used to record the execution of a program , The other one is used to verify whether the request is specified HTTPMethod, After creation, use the defined Chain Function http.HandlerFunc And the middleware application on it , Middleware will be executed in the order of adding , Finally, execute to the processing function . The complete code is as follows :
package mainimport ( "fmt" "log" "net/http" "time")type Middleware func(http.HandlerFunc) http.HandlerFunc// Record each URL The execution time of the request func Logging() Middleware { // Create middleware return func(f http.HandlerFunc) http.HandlerFunc { // Create a new handler packing http.HandlerFunc return func(w http.ResponseWriter, r *http.Request) { // The processing logic of middleware start := time.Now() defer func() { log.Println(r.URL.Path, time.Since(start)) }() // Call the next middleware or the final handler The handler f(w, r) } }}// Verify that the request uses the specified HTTP Method, If not, return to 400 Bad Requestfunc Method(m string) Middleware { return func(f http.HandlerFunc) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.Method != m { http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest) return } f(w, r) } }}// Apply to http.HandlerFunc Processor middleware // Chain the processors in order http.HandleFunc call func Chain(f http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc { for _, m := range middlewares { f = m(f) } return f}// The final processing of the request http.HandlerFunc func Hello(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, "hello world")}func main() { http.HandleFunc("/", Chain(Hello, Method("GET"), Logging())) http.ListenAndServe(":8080", nil)author :m0_72382685
Game programming , A game development favorite ~
If the picture is not displayed for a long time , Please use Chrome Kernel browser .
边栏推荐
- Hangfire详解
- 0/1分数规划专题
- Linux Installation mysql5
- As the "only" privacy computing provider, insight technology is the "first" to settle in the Yangtze River Delta data element circulation service platform
- Flume configuration 2 - ganglia for monitoring
- SSH command and instructions
- NLP - GIZA++ 实现词对齐
- A great open source image watermarking solution
- Dynamics crm: among locally deployed servers, sandbox, unzip, VSS, asynchronous and monitor services
- 【编译原理】语义分析
猜你喜欢

Configuration du Flume 4 - source personnalisée + sink

Flume理论

数据链路层

Flutter calls Baidu map app to realize location search and route planning

As the "only" privacy computing provider, insight technology is the "first" to settle in the Yangtze River Delta data element circulation service platform

data link layer

Foxit software was invited to appear at the 2022 advanced manufacturing digital intelligence development forum

文件包含漏洞

Understanding of software test logic coverage

如何设置 Pod 到指定节点运行
随机推荐
Sword finger offer 66 Building a product array
[notes] take notes again -- learn by doing Verilog HDL – 014
.NetCore统一认证授权学习——Run(1)
Flume configuration 2 - ganglia for monitoring
NLP - giza++ implements word alignment
Flume ng configuration
偶然发现了另一种跨域方式,不知道有没有人这么玩过
软件工程—原理、方法与应用
Nutch2.1 distributed fetching
Withdrawal of user curve in qualified currency means loss
Flume配置3——拦截器过滤
Following the crowd hurts you
Sword finger offer 59 - ii Maximum value of the queue
proxmox集群节点崩溃处理
CorelDRAW2022全新版V24.1.0.360更新
Nutch2.1分布式抓取
Chapter II (physical layer)
Bigder:自动化测试工程师
社区访谈丨一个IT新人眼中的JumpServer开源堡垒机
【Try to Hack】vulnhub narak