当前位置:网站首页>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 .
边栏推荐
- 【编译原理】类型检查
- As the "only" privacy computing provider, insight technology is the "first" to settle in the Yangtze River Delta data element circulation service platform
- Real time tracking of bug handling progress of the project through metersphere and dataease
- The era of data security solutions
- Understanding of software test logic coverage
- [notes] take notes again -- learn by doing Verilog HDL – 008
- Detailed description of gaussdb (DWS) complex and diverse resource load management methods
- Sword finger offer 59 - I. maximum value of sliding window
- 【编译原理】语义分析
- How to set a pod to run on a specified node
猜你喜欢
![[compilation principle] syntax analysis](/img/9e/6b1f15e3da9997b08d11c6f091ed0e.png)
[compilation principle] syntax analysis

Three.js开发:粗线的画法

The list of winners in the classic Smurfs of childhood: bluedad's digital collection was announced

There is no small green triangle on the method in idea

Comparable比较器写法&ClassCastExcption类转换异常

Understanding of software test logic coverage

Finally, Amazon~
![[boutique] detailed explanation of Pinia](/img/94/d332e32dba54be3c2d3f6ff08a85fa.png)
[boutique] detailed explanation of Pinia

JMeter BeanShell explanation and thread calling

Regular expression series of mobile phone numbers
随机推荐
2021 CCPC 哈尔滨 J. Local Minimum (思维题)
Hangfire详解
Oracle11.2.0.4-rac cluster hang analysis record
proxmox集群节点崩溃处理
【摸鱼神器】UI库秒变低代码工具——表单篇(一)设计
Defense cornerstone in attack and defense drill -- all-round monitoring
CorelDRAW2022全新版V24.1.0.360更新
2021 CCPC 哈尔滨 E. Power and Modulo (思维题)
Notepad++--宏(记录操作过程)
社区访谈丨一个IT新人眼中的JumpServer开源堡垒机
Linux安装MySQL8
Zotero journal Automatic Matching Update Influencing Factors
[boutique] detailed explanation of Pinia
Zotero journal automatic matching update influence factor
3-2 host discovery - layer 3 discovery
Configuration du Flume 4 - source personnalisée + sink
mysql中explain语句查询sql是否走索引,extra中的几种类型整理汇总
【Try to Hack】vulnhub narak
Oracle reserved word query
Comparable比较器写法&ClassCastExcption类转换异常