Context package necessary to pass variables between handlers?

agolangf · · 576 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>Is a context package such as the one from Google or gorilla/context really needed to pass variables between handlers?</p> <p>I&#39;ve read a few blog posts and they seem to use some sort of context package to achieve this. Some solutions are implementing a global map with a mutex, or implementing a struct that embeds the ResponseWriter interface, but they still use a context package for storing the context.</p> <p>Would it be possible to just create a map[string]interface{} in a lead handler function, and pass this object to each following handler? <a href="http://play.golang.org/p/7OzWC-Rcx7">Here&#39;s an example of this idea</a>.</p> <p>As far as I can tell this should be thread/goroutine safe as the context is created within the currently running goroutine specific to each request.</p> <p>Sorry if this has been done and answered already, articles I&#39;ve seen seem to use some context package.</p> <p><strong>Update</strong></p> <p><a href="http://play.golang.org/p/s4Qqg63ZAC">Here&#39;s a working example</a> using the method above, but with a context.Context struct used to pass the variables instead of a map[string]interface{}. It also uses the httprouter package (won&#39;t compile on play.golang.org, just copy and run it locally or on a server and browse to /user).</p> <p>One caveat with that example, if you for instance changed the user ID in HandleUser, and read it again when it returns to the middleware, the middleware will not have the updated context value. In order for this to work, it seems you have to pass context.Context as a pointer.</p> <p>No pointer: <a href="http://play.golang.org/p/3z7msFjUBU">http://play.golang.org/p/3z7msFjUBU</a> Pointer: <a href="http://play.golang.org/p/zc7iXj1fSL">http://play.golang.org/p/zc7iXj1fSL</a></p> <p>Is using a pointer the proper way to achieve this functionality?</p> <p><strong>Update</strong></p> <p>Nevermind, can just pass a pointer to context <a href="http://play.golang.org/p/l7lu3aeGV3">http://play.golang.org/p/l7lu3aeGV3</a></p> <hr/>**评论:**<br/><br/>karnd01: <pre><p>Using context seems to be the standard approach in just about every framework or router..examples: </p> <ul> <li><a href="https://github.com/go-playground/lars/blob/master/context.go#L39" rel="nofollow">https://github.com/go-playground/lars/blob/master/context.go#L39</a></li> <li><a href="https://github.com/gin-gonic/gin/blob/develop/context.go#L538" rel="nofollow">https://github.com/gin-gonic/gin/blob/develop/context.go#L538</a></li> <li><a href="https://github.com/labstack/echo/blob/master/context.go#L21" rel="nofollow">https://github.com/labstack/echo/blob/master/context.go#L21</a></li> <li>... List goes on</li> </ul></pre>beeker1121: <pre><p>Ok, it&#39;s a little confusing still exactly how context.Context works, but I&#39;ll try using it since it seems to be the standard. Thank you!</p></pre>pcj2bs: <pre><p>We do this with our &#34;framework&#34; <a href="https://github.com/VividCortex/siesta" rel="nofollow">VividCortex/siesta</a>. Here&#39;s a blog post that explains it: <a href="https://www.vividcortex.com/blog/2015/06/01/siesta/" rel="nofollow">https://www.vividcortex.com/blog/2015/06/01/siesta/</a></p></pre>beeker1121: <pre><p>Awesome, thank you.</p></pre>daveddev: <pre><p>Try out <a href="https://github.com/codemodus/chain" rel="nofollow">https://github.com/codemodus/chain</a></p> <p>Further, here is a discussion regarding moving net/context into the standard library and adding a context-passing handler func def: <a href="https://groups.google.com/forum/m/#!msg/golang-dev/cQs1z9LrJDU/S_9pzcAhCgAJ" rel="nofollow">https://groups.google.com/forum/m/#!msg/golang-dev/cQs1z9LrJDU/S_9pzcAhCgAJ</a></p></pre>beeker1121: <pre><p>Thanks, seems like that chain package implements the same method but still uses the net/context package. Why is that? Is it just to have a place to store the context variables, rather than creating their own struct?</p></pre>daveddev: <pre><p>Yes. My goal was to use the most &#34;blessed&#34; (for lack of better term) approach.</p> <p>This great article: <a href="https://joeshaw.org/net-context-and-http-handler/" rel="nofollow">https://joeshaw.org/net-context-and-http-handler/</a> along with this post: <a href="https://blog.golang.org/context" rel="nofollow">https://blog.golang.org/context</a> and a distaste for burying the standard library without great cause brought me to a mindset which pushes away from http frameworks. There is no gain avoiding helpful libraries, but seeking a do-it-all http framework left me feeling somewhat duped and disconnected from the process of learning Go.</p> <p>As for a takeway from this, it would be useful to focus on the context.Context changes that are likely to happen within the standard library. Once a context-based http handler func definition is decided upon, chain and libraries like it will be able to more easily operate interchangeably.</p></pre>beeker1121: <pre><p>I appreciate it, and I agree, I prefer using as few libraries as possible. I&#39;ll keep my eye on that Google group thread for what they decide to do, thank you again!</p></pre>Grundlebuttskin: <pre><p>I like the way <a href="https://goji.io" rel="nofollow">goji</a> handles this. It&#39;s minimal and uses net/context. The middleware chaining is pretty simple too. Anything that takes a context has a suffix of C (HandleC, UseC). go get goji.io</p></pre>beeker1121: <pre><p>Is the context package required though? Is it just used, in goji and other packages, to store the context data/variables? In other words would it be ok to not use net/context and just use a map[string]interface{}?</p></pre>Grundlebuttskin: <pre><p>I started using context because appengine requires it, then I continued because I like goji. The result? I can run my code with goji on appengine. There are other libraries (especially middleware such as github.com/rs/cors) that use context. I can drop these into my code without modification. You can use a map (or whatever you want) to pass data between handlers, but your code may be incompatible with other libraries. I personally benefit from the compatibility, but if you don&#39;t, then do whatever works for you.</p></pre>beeker1121: <pre><p>Thank you for the explanation!</p></pre>pkieltyka: <pre><p>Have a look at <a href="https://github.com/pressly/chi" rel="nofollow">https://github.com/pressly/chi</a> too which is a small router that is built for net/context handlers and middlewares. It also has a nice composable API for middleware chains and subrouters. Here&#39;s an example with the whole shebang for a REST service: <a href="https://github.com/pressly/chi/blob/master/_examples/rest/main.go" rel="nofollow">https://github.com/pressly/chi/blob/master/_examples/rest/main.go</a></p></pre>tty5: <pre><p><code>net/context</code> is actually fairly close to what you are describing when you check the code: <a href="https://github.com/golang/net/blob/master/context/context.go#L138" rel="nofollow">https://github.com/golang/net/blob/master/context/context.go#L138</a></p> <p>and this is how you implement something very close to your example: <a href="https://github.com/pressly/chi#middleware-handlers" rel="nofollow">https://github.com/pressly/chi#middleware-handlers</a></p> <p>So why shouldn&#39;t you roll your own? It&#39;s likely that <code>context.Context</code> field will be added to <code>http.Request</code> in standard library&#39;s <code>net/http</code> in go 1.7</p></pre>beeker1121: <pre><p>Thanks!</p></pre>dsymonds: <pre><p>Why not just pass the arguments individually? Maybe you&#39;re attacking a peanut-sized problem with a sledgehammer.</p></pre>beeker1121: <pre><p>I was thinking about doing this, for instance just passing a User struct to the handler that needs it. Consider though using multiple middleware functions that do not have the User struct as a parameter. Each middleware function would have to pass this User struct in order for the end handler to receive it, and handlers of a different signature (different parameters) would be unable to use the same middleware.</p> <p>By using a map[string]interface{} for context and passing it by default to every handler, all handlers will have the same signature and can then be used with any middleware.</p></pre>mwholt: <pre><p>Wow, timely question, I was also revisiting this today.</p> <p>Here&#39;s a possible approach that&#39;s very, very simple: <a href="http://play.golang.org/p/DLLCCWUh1k" rel="nofollow">http://play.golang.org/p/DLLCCWUh1k</a></p> <p>But as Joe Shaw&#39;s article (linked in another comment) mentions, you lose the benefit of satisfying certain other interfaces implemented by the underlying responseWriter type which is unexported; but if those are not used, then it seems to be a simple, acceptable way to do things.</p></pre>beeker1121: <pre><p>Quick question on that, he says this in the article:</p> <blockquote> <p>There is also another hidden downside. There is a concrete value (with a type internal to package net/http) underlying the http.ResponseWriter that is passed into your handler. That value also implements additional interfaces from the net/http package. If you simply wrap http.ResponseWriter, your wrapper will not be implementing these additional interfaces.</p> <p>You must implement these interfaces with wrapper functions if you hope to match the base http.ResponseWriter’s functionality.</p> </blockquote> <p>Could you explain why that happens? I thought if a struct namelessly embeds http.ResponseWriter for instance, it inherits all of its methods.</p></pre>mwholt: <pre><p>&#34;Inherit&#34; is not quite the right word. When you embed a type into a struct, callers can use the embedded type&#39;s methods and fields on the embedding type. The difference from &#34;inheritance&#34; is that it doesn&#39;t go down the rabbit hole. In other words, embedding a ResponseWriter (an interface) gives the embedding type methods like Write and WriteHeader, but Go does not traverse the interface down to the underlying concrete type and also embed those methods and fields; so you literally get a ResponseWriter capabilities out of it but nothing more, not without asserting on the ResponseWriter itself.</p> <p>This is actually a good thing for simplicity and clear code. And in your case, frankly, it may not be an issue unless you&#39;re using those underlying interfaces in your code (but be careful making that assumption).</p></pre>beeker1121: <pre><p>So for instance, would the &#39;response&#39; struct defined but not exported in the net/http package be a &#39;concrete type&#39; that implements ResponseWriter, but also implements the CloseNotifier interface? As such, my own struct type implementing ResponseWriter would not be able to access the CloseNotifier methods unless I defined them?</p></pre>mwholt: <pre><p>Correct!</p> <p>You could &#34;wrap&#34; the underlying implementation on your own type as Joe suggests, but with CloseNotifier it&#39;s a little tricky, as he points out. </p></pre>beeker1121: <pre><p>Ok very cool, thanks so much</p></pre>beeker1121: <pre><p>Updated the post, can anyone answer the question about the context pointer?</p></pre>

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

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