Skip to content

Instantly share code, notes, and snippets.

@georgehao
Created January 5, 2018 12:08
Show Gist options
  • Save georgehao/699d0d1ecf947f97a0bdc7e782ff2ba5 to your computer and use it in GitHub Desktop.
Save georgehao/699d0d1ecf947f97a0bdc7e782ff2ba5 to your computer and use it in GitHub Desktop.
[golang]笔记

在成熟的语言java、python、php要获取这些参数应该来讲都非常简单,过较新的语言golang用获取这些个参数还是费了不少劲,特此记录一下。

golang版本:1.3.1 在贴代码之前如果能先理解一下golang http.request的三个属性Form、PostForm、MultipartForm应该能较好的理解代码,下面摘录一下。

Form、PostForm、MultipartForm说明 Form、PostForm、MultipartForm说明

简要说明一下

Form:存储了post、put和get参数,在使用之前需要调用ParseForm方法。 PostForm:存储了post、put参数,在使用之前需要调用ParseForm方法。 MultipartForm:存储了包含了文件上传的表单的post参数,在使用前需要调用ParseMultipartForm方法。

获取GET参数 网上比较常见的一个版本是:

1 2 3 4 r.ParseForm() if len(r.Form["id"]) > 0 { fmt.Fprintln(w, r.Form["id"][0]) } 其中r表示*http.Request类型,w表示http.ResponseWriter类型。

r.Form是url.Values字典类型,r.Form["id"]取到的是一个数组类型。因为http.request在解析参数的时候会将同名的参数都放进同一个数组里,所以这里要用[0]获取到第一个。

这种取法在通常情况下都没有问题,但是如果是如下请求则无法取到需要的值:

1 2 3 4

因为r.Form包含了get和post参数,并且以post参数为先,上例post参数和get参数都有id,所以应当会取到post参数2。虽然这种情况并不多见,但是从严谨的角度来看程序上还是应当处理这种情况。立马补上改进代码:

1 2 3 4 queryForm, err := url.ParseQuery(r.URL.RawQuery) if err == nil && len(queryForm["id"]) > 0 { fmt.Fprintln(w, queryForm["id"][0]) } 代码比较简单,就是分析url问号后的参数。事实上这个也是标准库ParseForm中关于get参数解析代码。

获取POST参数 这里要分两种情况:

普通的post表单请求,Content-Type=application/x-www-form-urlencoded 有文件上传的表单,Content-Type=multipart/form-data 第一种情况比较简单,直接用PostFormValue就可以取到了。

1 fmt.Fprintln(w, r.PostFormValue("id")) 第二种情况复杂一些,如下表单:

1 2 3 4 5

因为需要上传文件,所以表单enctype要设置成multipart/form-data。此时无法通过PostFormValue来获取id的值,因为golang库里还未实现这个方法:

golang中不能用PostForm获取post参数 golang中不能用PostForm获取post参数

幸好golang中可以提供了另外一个属性MultipartForm来处理这种情况。

1 2 3 4 5 6 7 r.ParseMultipartForm(32 << 20) if r.MultipartForm != nil { values := r.MultipartForm.Value["id"] if len(values) > 0 { fmt.Fprintln(w, values[0]) } } 感谢:在测试post的时候,一开始都是以第二种情况来测试的,所以造成了一个误区以为PostFormValue无法取到值。这里感谢@九头蛇龙 的纠正。

获取COOKIE参数 1 2 3 4 5 6 7 cookie, err := r.Cookie("id") if err == nil { fmt.Fprintln(w, "Domain:", cookie.Domain) fmt.Fprintln(w, "Expires:", cookie.Expires) fmt.Fprintln(w, "Name:", cookie.Name) fmt.Fprintln(w, "Value:", cookie.Value) } r.Cookie返回*http.Cookie类型,可以获取到domain、过期时间、值等数据。

小结 在折腾的过程中看了下net/http包中的源码,感觉在web开发中还是有很多不完善的地方。作为使用者来讲,最希望就是直接通过一个方法取到相应的值就可以了,期待官方团队尽早完善。

  1. 当前go源文件中, 每一个被Import的包, 按其在源文件中出现顺序初始化。

  2. 如果当前包有多个init在不同的源文件中, 则按源文件名以字典序从小到大排序,小的先被执行到, 同一包且同一源文件中的init,则按其出现在文件中的先后顺序依次初始化; 当前包的package level变量常量也遵循这个规则; 其实准确来说,应是按提交给编译器的源文件名顺序为准,只是在提交编译器之前, go命令行工具对源文件名按字典序排序了。

  3. init只可以由go runtine自已调用, 我们在代码中不可以显示调用,也不可以被引用,如赋给a function variable。

  4. 包A 引入包B , 包B又引入包C, 则包的初始化顺序为: C -> B -> A

  5. 引入包,必须避免死循环,如 A 引 B , B引C, C引A.

  6. 一个包被其它多个包引入,如A -> B ->C 和 H -> I -> C , C被其它包引了2次, 但是注意包C只被初始化一次。

  7. 另一个大原则, 被依赖的总是先被初始化,当然呀。

  8. main包总是被最后一个初始化,这很好理解,因为它总是依赖别的包。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment