What is the correct way to validate params on a POST request?

xuanbao · · 637 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I know there are some libraries out that that do this, but I&#39;d like to learn how to do it myself.</p> <p>EDIT: I think I should explain a bit more. All of the parameters are kept in a struct, not an array, slice, or list. Is there a way to iterate through a struct&#39;s fields?</p> <hr/>**评论:**<br/><br/>drvd: <pre><p>So just do it: Validate them. </p></pre>thecodethinker: <pre><p>so... if/else for 50+ params... that seems... wrong It&#39;s not DRY at all and it seems like go was made to promote patters like DRY</p></pre>barsonme: <pre><p>I think the biggest piece of evidence against Go promoting DRY is error handling. <code>if err != nil...</code> is definitely not DRY code.</p></pre>perihelion9: <pre><p>But you actually take different actions when errors are present, sometimes you can recover, sometimes you wrap the error and return it, sometimes you log it an continue, etc. The <code>if err != nil</code> is used specifically because the language designers know that it&#39;s good practice to handle specific errors in a non-generic way.</p> <p>I&#39;ve sometimes thought that Go could benefit from an <code>assert</code>-like keyword, but that would really just confuse things. Why <code>if err != nil</code> sometimes, but not all the time? </p></pre>mwsherman: <pre><p>I don’t think Go promotes DRY. If you have two pieces of code that overlap (say) 90%, I think the Go way is: have two pieces of code.</p> <p>There are several places in the standard library where they’ve copied and pasted a small routine instead of importing the package that contained it.</p> <p>Personally, I’ve come to see DRY as a thing that might happen <em>after</em> you’ve implemented the right thing, and only when obvious redundancies stand out. DRY is not a goal, it’s an option.</p> <p>I speculate, and agree with the notion, that the Go team sees DRY can become an antipattern that leads to bad things like highly overloaded methods and unnecessary dependencies. Overfactoring, if you will.</p></pre>dchapes: <pre><blockquote> <p>There are several places in the standard library where they’ve copied and pasted a small routine instead of importing the package that contained it.</p> </blockquote> <p>Every such case I&#39;m aware of is simply to avoid extraneous package interdependencies. I.e. they don&#39;t want everything that imports <code>fmt</code> to implicitly end up indirectly importing/depending on half the standard packages.</p></pre>quiI: <pre><p>I dont think that&#39;s really the &#34;spirit&#34; of DRY. </p> <p>DRY means dont have repeated logic as in you shouldn&#39;t repeat &#34;Cats have 9 lives&#34;.</p> <p>No (non-trivial) program in the world has one place that says &#34;if there is an error, do this&#34;. Because &#34;this&#34; varies. </p> <p>In respect to OP. Validation failures are not &#34;errors&#34; per se. You wouldn&#39;t see</p> <pre><code> if err !=nil </code></pre> <p>You would see</p> <pre><code> if myStruct.paramX != &#34;foobar&#34; </code></pre> <p>Which is what.. reality is! I dont see how this is especially different from any other language. You have to define <em>somewhere</em> what is valid, and what isn&#39;t</p></pre>thecodethinker: <pre><p>That is true</p></pre>010a: <pre><p>I&#39;ve heard various arguments that Go is a poor language to use for web programming. It is somewhat antithetical to its original design of being a systems programming language, but its definitely easy to confuse the two as there is overlap. </p> <p>In my mind, using Go for web dev is as enjoyable as using Java, which is to say it isn&#39;t. </p></pre>klauspost: <pre><p>Agree - start by validating them, and then observe what you do a lot and refactor it. Go is so easy to refactor, that it is perfectly fine to write stuff first and refactor when you know what you need. </p> <p>For instance, in a rather big web-app we started with huge functions for each API call, basically validating every parameter. After some time we generalized a lot of stuff, so:</p> <ul> <li>Models can have a Validator interface, that can return one or more errors. Run before saving to DB.</li> <li>We use other interfaces to check common properties (does the object have custom properties, tags, etc)</li> <li>All ID types - links to other objects, which have a common interface are automatically validated.</li> <li>All absolute times are converted to UTC.</li> <li>Updates/patches are sent through a common validator, which can also filter out no-ops (input=existing data), and send notifications.</li> <li>Data models are separated into internal/user creatable/user updatable structs, so we safely can de-serialize json, and not worry about users overwriting internal parts of documents. Embedding is awesome for this!</li> </ul> <p>Example: <a href="https://gist.github.com/klauspost/da89bbcba090dcd5c0f4" rel="nofollow">https://gist.github.com/klauspost/da89bbcba090dcd5c0f4</a></p> <p>This may seem like a lot, but each feature was only implemented after we realized that we were doing too much of the same thing, or that we were risking missing important checks.</p> <p>But the important part is NOT to plan everything out beforehand, but do it incrementally. That way you can evaluate on the fly if your refactoring brings better readability/security.</p></pre>elithrar_: <pre><blockquote> <p>Models can have a Validator interface, that can return one or more errors. Run before saving to DB.</p> </blockquote> <p>This is precisely what I do. Marshal <code>r.FormValue</code> into a struct with either gorilla/schema or <a href="https://github.com/goji/param" rel="nofollow">goji/param</a> and define a <code>Validate()</code> method on my struct that returns useful errors.</p> <p>I use <a href="https://github.com/kat-co/vala" rel="nofollow">kat-co/vala</a> to validate each field (length, not empty, etc) and construct useful error messages but it&#39;s not that far from just writing <code>if len(s.Field) &lt; fieldLen { ... }</code>. I have a couple of custom validators in cases where I have a slice of acceptable values that I check against.</p> <p>You <em>can</em> reflect over fields in a struct but it&#39;s: a) madness; b) leans towards using struct tags, which are already heavily used by a lot of apps and start to get very unwieldy, and c) a lot of error-prone code.</p></pre>oefig: <pre><p>Commenting to save this for later</p></pre>Growlizing: <pre><p>If it&#39;s small, just validate it.</p> <p>If its larger, I do something ala this:</p> <pre><code>f := formData{ name: r.FormValue(&#34;name&#34;), email: r.FormValue(&#34;email&#34;), ssn: r.FormValue(&#34;ssn), } if !f.Valid() { // error here } </code></pre></pre>mwholt: <pre><p>Write a function. (Don&#39;t use struct tags, unless your needs are very straightforward and limited in scope.)</p></pre>barsonme: <pre><p>Couple ways. First is to manually get each form input&#39;s value with <a href="http://golang.org/pkg/net/http/#Request.PostFormValue" rel="nofollow">r.PostFormValue</a>. </p> <p>Another would be something like the Gorilla library&#39;s Schema package that reflects over the url.Values{} and marshals them into a struct that represents the form.</p> <p>Those are the 2 primary ways. As for the actual validation (e.g., is the provided input a valid email? Does the comment contain profanity? Etc.) that&#39;ll depend what you&#39;re looking to do.</p> <p>Emails can be parsed to see if they contain an at sign and a period and only contain valid characters (could use a regex but I&#39;d advise against it). Profanity could be checked against a map or you could bounce each word to a redis db...</p> <p>Hope that helps a little.</p></pre>calebdoxsey: <pre><blockquote> <p>Is there a way to iterate through a struct&#39;s fields?</p> </blockquote> <p>Use <code>reflect</code> with <code>NumField</code> and <code>Field</code>. You could use tags to annotate validation ala json.</p> <p>I prefer a more straightforward style. Create a few helper functions for the various types of fields you want:</p> <pre><code>obj.PhoneNumber, err = GetPhoneNumber(req, &#34;field-name&#34;) if err == nil { obj.Email, err = GetEmail(req, &#34;field-name&#34;) } if err == nil { obj.Country, err = GetCountry(req, &#34;field-name&#34;) } // and so on </code></pre> <p>If that&#39;s too repetitive a map would be more appropriate than a struct. Go&#39;s database libraries rarely require structs to work with so you could actually make the whole process very dynamic.</p> <p>But I like the slightly more verbose nature of the struct. The type safety catches a lot of mistakes. It&#39;s a little extra typing, but we&#39;re only talking an extra 20-30 minutes. And once written adding additional fields is trivial.</p></pre>

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

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