Can someone help explain this middleware to me?

agolangf · · 381 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>From the book &#34;Level up your web apps with Go&#34; is this file that contains &#34;middleware&#34; used in the application. Here it is:</p> <pre><code>package main import &#34;net/http&#34; type Middleware []http.Handler func (m *Middleware) Add(handler http.Handler) { *m = append(*m, handler) </code></pre> <p>}</p> <pre><code>func (m Middleware) ServeHTTP(w http.ResponseWriter, r *http.Request) { // Wrap the supplied ResponseWriter mw := NewMiddlewareResponseWriter(w) // Loop through all of the registered handlers for _, handler := range m { // Call the handler with our MiddlewareResponseWriter handler.ServeHTTP(mw, r) // If there was a write, stop processing if mw.written { return } } // If no handlers wrote to the response, it’s a 404 http.NotFound(w, r) </code></pre> <p>}</p> <pre><code>type MiddlewareResponseWriter struct { http.ResponseWriter written bool } func NewMiddlewareResponseWriter(w http.ResponseWriter) *MiddlewareResponseWriter { return &amp;MiddlewareResponseWriter{ ResponseWriter: w, } } func (w *MiddlewareResponseWriter) Write(bytes []byte) (int, error) { w.written = true return w.ResponseWriter.Write(bytes) } func (w *MiddlewareResponseWriter) WriteHeader(code int) { w.written = true w.ResponseWriter.WriteHeader(code) } </code></pre> <p>I&#39;m not entirely clear on what the purpose of this middleware is. What is the purpose looping through the Handlers and writing to the boolean flags on the MiddlewareResponseWriter types? Maybe this question is more about the purpose of middleware in general, but it would be good to have a Go-specific take on it. Thanks in advance.</p> <hr/>**评论:**<br/><br/>nhooyr: <pre><p>This is a poor way to do middleware. See <a href="https://justinas.org/alice-painless-middleware-chaining-for-go/">https://justinas.org/alice-painless-middleware-chaining-for-go/</a></p></pre>sairamk: <pre><p>I was looking at Alice library. I have been trying to find some well written examples. </p> <p>Is there a way not to pass the control to the next HttpHandler? I am looking for suggestions on implementing both the cases</p> <ol> <li>acting like a wrapper like a timer wrapper or gzip wrapper (<a href="https://github.com/gorilla/handlers" rel="nofollow">https://github.com/gorilla/handlers</a>) </li> <li>acting like a stopgap like IP blacklisting </li> </ol></pre>gohacker: <pre><blockquote> <p>This is a poor way to do middleware.</p> </blockquote> <p>Care to elaborate?</p></pre>thesilentwitness: <pre><p>If this is in a book I recommend a new book.</p></pre>gohacker: <pre><p>Care to elaborate?</p></pre>joncalhoun: <pre><p>This isn&#39;t the best set of MW I have ever seen, but I&#39;ll try to explain what I believe is going on.</p> <p>Let&#39;s say you have an application that needs to do the following in this order:</p> <ol> <li>Verify that the IP isn&#39;t blacklisted</li> <li>Verify that the user is logged in</li> <li>Show them the page</li> </ol> <p>This middleware is attempting to make this possible using the standard http.Handler interface by checking to see if any writes have been made along the way to the response, and then assuming that if a write has occurred, that the request should be terminated. So let&#39;s look at step 1.</p> <p>Step 1: Verify that the IP isn&#39;t blacklisted</p> <pre><code>type IPChecker struct {} func (i IPChecker) ServeHTTP(w http.ResponseWriter, r *http.Request) { if badIP(r) { w.Write(...) } } </code></pre> <p>If no write occurs and the IP is good, the middleware would then move on to the next thing in the stack. If it does write, the middleware will know because the boolean flag will be set to true, so it will stop and won&#39;t move to step 2.</p> <p>I think most developer generally prefer to instead do something like this:</p> <pre><code>func CheckIP(h http.Handler) http.Handler { return http.Handler(func(w http.ResponseWriter, r *http.Request) { if badIP(r) { w.Write(...) return } h(w, r) }) } </code></pre> <p>In this case your middleware (CheckIP) gets to explicitly decide if it is going to call the next handler in the chain, and that isn&#39;t dictated purely by whether or not a write occurs. It is also <em>much</em> clearer when the next MW gets called because you see it being called, whereas w/ the MW you included it isn&#39;t clear at first that writes will stop other MW from being called.</p></pre>ConfuciusBateman: <pre><p>Thank you so much, this really helped. So as I understand it now, middleware is essentially a way of sequencing HTTP requests (or is it necessarily limited to HTTP requests, maybe just general actions taken within the web application?) and making certain decisions along the course of the sequence as needed? </p></pre>joncalhoun: <pre><p>Looks like your question was answered below, but feel free to ask further questions if you have them.</p> <p>If you are coming from another background like Rails you can think of middleware as a <code>before_filter</code> or <code>before_action</code> often declared in a controller concern (see <a href="http://elegantbrew.tumblr.com/post/70990048275/controller-concerns-in-rails-4" rel="nofollow">http://elegantbrew.tumblr.com/post/70990048275/controller-concerns-in-rails-4</a> for an example). There are other forms of Middleware in Rails, but what you expect to be a <code>before_action</code> in Rails will often be a MW in Go. </p></pre>xy8_t_rel_: <pre><p>The purpose of &#34;middlewares&#34;, or filters is code reuse. For instance you want to make sure the user is authenticated. You&#39;re not going to write the same logic for each handler when you can just make that logic &#34;sit on top&#34; of all your handlers. </p> <p>This piece of code isn&#39;t very good, because it uses a erratic condition to break the middleware queue . Each middleware should be responsible for deciding wether the middleware queue should be broken or not. That&#39;s why the usual pattern for a middleware queue is the following</p> <pre><code>func IsAuthenticatedMiddleware(next http.HandlerFunc)http.HandlerFunc{ return func(rw http.ResponseWriter,*r http.Request){ if userIsAuthenticated(){ next(rw,r) return } rw.WriteHeader(401) } } </code></pre> <p>and then in your code</p> <pre><code>server.HandleFunc(&#34;/secure&#34;,IsAuthenticatedMiddleware(MyHandlerfunc)) </code></pre> <p>Now of imagine you have 10 middlewares you want to queue before your handler. You need to come up with an abstraction, that&#39;s what the author of the book tried to do. </p></pre>ConfuciusBateman: <pre><p>This makes even more sense now! This sub is awesome. Putting middleware in context of avoiding logic duplication really helps. </p></pre>gohacker: <pre><blockquote> <p>This piece of code isn&#39;t very good, because it uses a erratic condition to break the middleware queue . Each middleware should be responsible for deciding wether the middleware queue should be broken or not.</p> </blockquote> <p>It is for the middleware to decide whether it writes to the ResponseWriter or not (= whether the middleware queue should be broken or not). It is a good, solid approach to writing middleware, however different it might be.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

381 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传