<p>I have seen a lot of debate over this recently, so I am genuinely curious if others could share from actual experience whether or not using control variables in <code>context.Value</code> has proven to be problematic for their applications or not. Basically I want to discuss questions like:</p>
<ul>
<li>Has it made your application harder to reason about?</li>
<li>Has it made type safety harder to ensure?</li>
<li>What other approaches have you tried and how have they fared? Eg Goa appears to create a custom context type for each request to avoid this - <a href="https://github.com/goadesign/goa-cellar/blob/master/controllers/bottle.go#L45">https://github.com/goadesign/goa-cellar/blob/master/controllers/bottle.go#L45</a>. This seems to work great for generated code, but it might become cumbersome for manually written code.</li>
</ul>
<p><strong>Background</strong>:</p>
<p>I am specifically referring to web apps and the <a href="https://golang.org/pkg/net/http/#Request.Context">Request.Context()</a> that is available, but other examples are likely welcome. I mostly think of this with web apps because it is a fairly common practice with middleware.</p>
<p>I would prefer to limit the discussion to request-scoped variables (as this is what context was intended for) so I am thinking of things like looking up the user who made a request and storing it in <code>context.Value</code>. Taking something not request scoped (eg a DB connection) and storing it as a context value is (I believe) pretty unanimously considered to be a bad practice, so I want to focus on the gray area and learn from others experience.</p>
<p>If you don't know what I mean by control variables, this article by Jack Lindamood should help - <a href="https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39#.ss5je3m6j">https://medium.com/@cep21/how-to-correctly-use-context-context-in-go-1-7-8f2c0fafdf39#.ss5je3m6j</a>. In short, they are variables that are used not just to inform your application of something (eg a request ID informs a logger of the ID to log) but variables that can actually be used to control the logical flow of your application. The simplest example I can think of is again a user object - you might use it later in your stack to determine if a user has access to an admin only feature, which means your code will change control paths depending on what that value is.</p>
<hr/>**评论:**<br/><br/>kardianos: <pre><p>Context.Value is great. It has not made my programs any harder to reason about; in fact, it makes it easier to reason about because I don't have to use non-standard way to pipe in request scoped values.</p></pre>joncalhoun: <pre><p>What type of values do you store or can you provide any further details about what LL you store that seems to work well here?</p>
<p>Also thanks for sharing about your experience so far :)</p></pre>epiris: <pre><p>I think conceptually context values are okay, note the word "values" as in more than one. I disagree with several implementation details of the entire package though that I think affect the usability as a whole including that of values.</p>
<p>I won't deep dive here, mostly I just wish the context interface was implemented more in the spirit of package io. In the current implementation it's impossible to extend without caveats due to Stdlib asserting concrete types. I.e. For cancelation / propagation I don't know why they don't check if a context.Canceler{ Cancel() } interface is implemented.</p></pre>xy8_t_rel_: <pre><p>context.Value signature is mistake AND an acknowledgment by the Go team that Go's type system is broken and doesn't always allow writing compile time type safe code.</p>
<p>context itself was created because libraries like gorilla/session which use global values to hold request scoped values were unable to cleanup http requests automatically ( one has to wrap his app into a specific "middleware" provided by gorilla toolkit ). context has a channel that can signal a global variable to erase a request value.</p>
<p>I always thought that the heart of the problem (if one ignores Go type system) is http.HandlerFunc's signature. Had they choose to pass an interface instead ( type Context interface { ResponseWriter() Request() } ) People could have at the very list pass whatever type they wanted to handlers, provided it implements that interface . Then they could have extended that interface with their own Methods ( type MyContext { GetSession() Context } ). </p>
<p>It's still "voodoo" at runtime but less "voodoo" than context.Value(interface{})interface{} </p>
<p>Like I said previously we'll still be having that discussion in 10 years, because something feels off, no matter what people say or pretend to ignore. </p>
<blockquote>
<p>I would prefer to limit the discussion to request-scoped variables</p>
</blockquote>
<p>But what does it mean? Most variables in a handler are expected to live only during the request/response cycle. The only exception would be a pool of DB connections which should be thread safe and global. So in theory, saying something must be request scoped in order to be in the context makes very little sense, as most values in a handler are expected to be request scoped at first place. Why would some validator value for a DB model created inside a middleware, would be less request scoped than a session ID if it's life time is only the request? </p>
<p>We shouldn't be having these kind of discussions at first place yet here we are.</p></pre>joncalhoun: <pre><blockquote>
<p>But what does it mean?</p>
</blockquote>
<p>You pointed to an example (db connections) and there are others like it (eg templates your app uses). Those are pretty obviously not suited for context values.</p>
<p>The purpose of this post wasn't to start a thread where we point out the faults of the language, but to instead to discuss the types of issues people using context values this way have run into. Is it harder to understand where values are coming from? Is not having compile-time type checks on values you store in the context values actually causing issues to pop up in production?</p>
<p>While I understand you have a strong opinion on this, you aren't really contributing to the discussion I wanted to have :(</p></pre>xy8_t_rel_: <pre><p>Well the answer is simple, anything can go into the context because it is untyped. You shouldn't have to ask yourself that question at first place, that's the point of my message, yet because of Go shortcomings, you are doing that. So you only can decide what goes into context. </p></pre>
What are your practical experiences with using control variables in context.Value?
xuanbao · · 440 次点击这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传