当前位置:   article > 正文

[golang Web开发] 3.golang web开发:处理请求_golang 网站开发 处理请求参数

golang 网站开发 处理请求参数

简介

Go语音的net/http包提供了一系列用于表示HTTP报文的结构,可以使用它处理请求和发送响应,其中Request结构代表了客户端发送的请求报文,下面是Request讲解

  1. type Request struct {
  2. // Method指定HTTP方法(GET、POST、PUT等)。对客户端,""代表GET。
  3. Method string
  4. // URL在服务端表示被请求的URI,在客户端表示要访问的URL。
  5. //
  6. // 在服务端,URL字段是解析请求行的URI(保存在RequestURI字段)得到的,
  7. // 对大多数请求来说,除了Path和RawQuery之外的字段都是空字符串。
  8. // (参见RFC 2616, Section 5.1.2)
  9. //
  10. // 在客户端,URL的Host字段指定了要连接的服务器,
  11. // 而Request的Host字段(可选地)指定要发送的HTTP请求的Host头的值。
  12. URL *url.URL
  13. // 接收到的请求的协议版本。本包生产的Request总是使用HTTP/1.1
  14. Proto string // "HTTP/1.0"
  15. ProtoMajor int // 1
  16. ProtoMinor int // 0
  17. // Header字段用来表示HTTP请求的头域。如果头域(多行键值对格式)为:
  18. // accept-encoding: gzip, deflate
  19. // Accept-Language: en-us
  20. // Connection: keep-alive
  21. // 则:
  22. // Header = map[string][]string{
  23. // "Accept-Encoding": {"gzip, deflate"},
  24. // "Accept-Language": {"en-us"},
  25. // "Connection": {"keep-alive"},
  26. // }
  27. // HTTP规定头域的键名(头名)是大小写敏感的,请求的解析器通过规范化头域的键名来实现这点。
  28. // 在客户端的请求,可能会被自动添加或重写Header中的特定的头,参见Request.Write方法。
  29. Header Header
  30. // Body是请求的主体。
  31. //
  32. // 在客户端,如果Body是nil表示该请求没有主体买入GET请求。
  33. // Client的Transport字段会负责调用Body的Close方法。
  34. //
  35. // 在服务端,Body字段总是非nil的;但在没有主体时,读取Body会立刻返回EOF。
  36. // Server会关闭请求的主体,ServeHTTP处理器不需要关闭Body字段。
  37. Body io.ReadCloser
  38. // ContentLength记录相关内容的长度。
  39. // 如果为-1,表示长度未知,如果>=0,表示可以从Body字段读取ContentLength字节数据。
  40. // 在客户端,如果Body非nil而该字段为0,表示不知道Body的长度。
  41. ContentLength int64
  42. // TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。
  43. // 本字段一般会被忽略。当发送或接受请求时,会自动添加或移除"chunked"传输编码。
  44. TransferEncoding []string
  45. // Close在服务端指定是否在回复请求后关闭连接,在客户端指定是否在发送请求后关闭连接。
  46. Close bool
  47. // 在服务端,Host指定URL会在其上寻找资源的主机。
  48. // 根据RFC 2616,该值可以是Host头的值,或者URL自身提供的主机名。
  49. // Host的格式可以是"host:port"。
  50. //
  51. // 在客户端,请求的Host字段(可选地)用来重写请求的Host头。
  52. // 如过该字段为"",Request.Write方法会使用URL字段的Host。
  53. Host string
  54. // Form是解析好的表单数据,包括URL字段的query参数和POST或PUT的表单数据。
  55. // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
  56. Form url.Values
  57. // PostForm是解析好的POST或PUT的表单数据。
  58. // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
  59. PostForm url.Values
  60. // MultipartForm是解析好的多部件表单,包括上传的文件。
  61. // 本字段只有在调用ParseMultipartForm后才有效。
  62. // 在客户端,会忽略请求中的本字段而使用Body替代。
  63. MultipartForm *multipart.Form
  64. // Trailer指定了会在请求主体之后发送的额外的头域。
  65. //
  66. // 在服务端,Trailer字段必须初始化为只有trailer键,所有键都对应nil值。
  67. // (客户端会声明哪些trailer会发送)
  68. // 在处理器从Body读取时,不能使用本字段。
  69. // 在从Body的读取返回EOF后,Trailer字段会被更新完毕并包含非nil的值。
  70. // (如果客户端发送了这些键值对),此时才可以访问本字段。
  71. //
  72. // 在客户端,Trail必须初始化为一个包含将要发送的键值对的映射。(值可以是nil或其终值)
  73. // ContentLength字段必须是0或-1,以启用"chunked"传输编码发送请求。
  74. // 在开始发送请求后,Trailer可以在读取请求主体期间被修改,
  75. // 一旦请求主体返回EOF,调用者就不可再修改Trailer。
  76. //
  77. // 很少有HTTP客户端、服务端或代理支持HTTP trailer。
  78. Trailer Header
  79. // RemoteAddr允许HTTP服务器和其他软件记录该请求的来源地址,一般用于日志。
  80. // 本字段不是ReadRequest函数填写的,也没有定义格式。
  81. // 本包的HTTP服务器会在调用处理器之前设置RemoteAddr为"IP:port"格式的地址。
  82. // 客户端会忽略请求中的RemoteAddr字段。
  83. RemoteAddr string
  84. // RequestURI是被客户端发送到服务端的请求的请求行中未修改的请求URI
  85. // (参见RFC 2616, Section 5.1)
  86. // 一般应使用URI字段,在客户端设置请求的本字段会导致错误。
  87. RequestURI string
  88. // TLS字段允许HTTP服务器和其他软件记录接收到该请求的TLS连接的信息
  89. // 本字段不是ReadRequest函数填写的。
  90. // 对启用了TLS的连接,本包的HTTP服务器会在调用处理器之前设置TLS字段,否则将设TLS为nil。
  91. // 客户端会忽略请求中的TLS字段。
  92. TLS *tls.ConnectionState
  93. }

Request类型代表一个服务端接受到的或者客户端发送出去的HTTP请求。

Request各字段的意义和用途在服务端和客户端是不同的。除了字段本身上方文档,还可参见Request.Write方法和RoundTripper接口的文档

一.获取请求url

Request结构体中的URL字段用于表示请求行中包含的URL,该字段是一个指向url.URL结构的指针,下面是URL结构讲解

import "net/url"

url包解析URL并实现了查询的逸码

 (1).Path字段

        * 获取请求的URL

        * eg:http:localhost:8080/hello?username=admin&password=123456, 通过r.URL.Path  只能得到/hello

(2).RawQuery字段

        * 获取请求的URL后面?后面的查询字符串

        * eg:http:localhost:8080/hello?username=admin&password=123456, 通过r.URL.RawQuery得到的是 username=admin&password=123456

二.获取请求头中的信息

通过Request结果中的Header字段来获取请求头中的所有信息,Header字段的类型是Header类型,而Header类型是一个map[string][]string,下面是Header类型及它的方法

三.获取请求体中的信息

请求和响应的主体都是有Request结构中的body字段表示,这个字段的类型是io.ReadCloser接口,该接口包含了Reader接口和Closer接口,Reader接口拥有Read方法,Closer接口拥有Close方法

(1).由于GET请求没有请求体,所以需要在HTML页面创建一个form表单,通过指定method="post"来发送一个POST请求

form.html提交如下:

  1. <form action="http://localhost:8080/getBody" method="POST">
  2. 用户名:<input type="text" name="username" value="admin"><br/>
  3. 密码:<input type="password" name="password" value="123456"><br/>
  4. <input type="提交">
  5. </form>

 main.go获取客户端提交的信息,并解析如下:

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. //获取内容长度
  3. length := r.ContentLenth
  4. //创建一个切片
  5. body := make([]byte, length)
  6. //读取请求体
  7. r.Body.Read(body)
  8. fmt.Fprintln(w, "请求体中的内容是:", string(body))
  9. }

四.获取请求参数

通过net/http库中的Request结构的字段以及方法来获取请求URL后面的请求参数以及form表单提交的请求参数

1.Form字段 

(1).类型是url.Values类型,Form是解析好的表单数据,包括URL字段的query参数和POST或PUT表单数据

 包net/url下url.Values

 (2).Form字段只有在调用Request的ParseForm后才有效,在客户端,会忽略请求中的本字段而使用Body替代

代码: 

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. //解析表单
  3. r.ParseForm()
  4. //获取请求参数
  5. fmt.Fprintln(w, "请求参数:", r.Form)
  6. }

注意:在执行r.Form之前一定要调用r.ParseForm方法对表单进行解析 

结果:请求参数为:map[password:[123456]] username:[admin]] 

2. 如果对form表单做一些修改,在action属性的URL后面也添加相同的请求参数

如果对form表单做一些修改,在action属性的URL后面也添加相同的请求参数,如下: 

        action="http://localhost:8080/getBody?pwd=123456&username=zhangsan"

则执行结果如下:

        map[password:[123456]] pwd:[123456] username:[admin zhangsan]] 

可以发现:

        * 表单中的请求参数 username 和 URL 中的请求参数 username 都获取到了,而且表单中的请求参数的值排在 URL 请求参数值的前面

        * 如果此时只想获取表单中的请求参数该怎么办呢?

        * 那就需要使用 Request 结构中的PostForm字段,但是PostForm字段只支持 application/x-www-form-urlencoded编码,如果 form 表单的enctype属性值为 multipart/form-data,那么使用PostForm字段无法获取表单中的数据,此时需要使用MultipartForm字段

        * 说明: form 表单的enctype属性默认值为application/x-www-form-urlencoded编码.,实现上传文件时需要将该属性的值设置为multipart/form-data编码格式

 3.FormValue方法和PostFormValue方法

代码如下: 

  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. //创建处理器函数
  7. func handler(w http.ResponseWriter, r *http.Request) {
  8. _, err := fmt.Fprintln(w, "请求地址:", r.URL.Path)
  9. _, err = fmt.Fprintln(w, "请求地址中后面的查询字符串:", r.URL.RawQuery)
  10. _, err = fmt.Fprintln(w, "请求地址中请求头数据:", r.Header)//返回map[string][][string]
  11. _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的信息:", r.Header["Accept-Encoding"]) //返回map切片
  12. _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的属性值:", r.Header.Get("Accept-Encoding"))//返回string
  13. 获取请求体中内容长度
  14. //len := r.ContentLength
  15. 创建byte切片
  16. //body := make([]byte, len)
  17. 将请求体中的内容读取到byte切片中
  18. //_, err = r.Body.Read(body)
  19. 浏览器中显示请求体中的内容
  20. //_, err = fmt.Fprintln(w, "浏览器中显示请求体中的内容:",string(body))
  21. //在解析表单r.Form之前,需执行以下方法
  22. //err = r.ParseForm()
  23. 如果form表单的action属性值的地址中也有与form表单参数名相同的请求参数,那么参数值都可以得到
  24. 并且form表单中的参数值会在url参数值的前面
  25. //_, err = fmt.Fprintln(w, "请求表单请求参数:", r.Form)
  26. //_, err = fmt.Fprintln(w, "post请求中Form表单请求参数:", r.PostForm)
  27. //通过直接调用FormValue,PostFormValue直接获取请求参数中的值
  28. _, err = fmt.Fprintln(w, "Url中username请求参数的值:", r.FormValue("username"))
  29. _, err = fmt.Fprintln(w, "post请求中Form表单name请求参数:", r.PostFormValue("name"))
  30. if err != nil {
  31. fmt.Println(err)
  32. }
  33. }
  34. func main() {
  35. http.HandleFunc("/req", handler)
  36. err := http.ListenAndServe(":8080", nil)
  37. if err != nil {
  38. fmt.Println(err)
  39. }
  40. }
  1. <html>
  2. <head>
  3. <meta charset="UTF-8">
  4. </head>
  5. </head>
  6. <body>
  7. <form action="http://127.0.0.1:8080/req?username=张三" method="post">
  8. 用户名:<input type="text" name="name"/><br/>
  9. 用密码:<input type="text" name="password"/><br/>
  10. <input type="submit">
  11. </form>
  12. </body>
  13. </html>

五.给客户端响应

前面讲解了如何使用处理器中的*http.Request处理用户的请求,下面说一下如何使用http.ResponseWriter来给用户响应        

1.给客户端响应一个字符串

(1).处理器中的代码

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. w.Write([]byte("你的请求服务端已收到"))
  3. }

(2).浏览器中的结果

你的请求服务端已收到

(3).响应报文的内容

HTTP/1.1 200 OK

Date:Fri, 10 Aug 2022 01:01:01 GMT

Content-Length: 27

Content-Type: text/plain; charset=utf-8 

2.给客户端响应一个HTML页面 

(1).处理器中的代码

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. html := `<html>
  3. <head>
  4. <title>测试响应内容为网页</title>
  5. <meta charset="utf-8"/>
  6. </head>
  7. <body>
  8. 以网页形式响应
  9. </body>
  10. </html>`
  11. w.Write([]byte(html))
  12. }

(2).浏览器中的结果

 以网页形式响应

通过在浏览器中右键->查看网页代码可以发现:有一个html页面

(3).响应报文中的内容

HTTP/1.1 200 OK

Date:Fri, 10 Aug 2022 01:01:01 GMT

Content-Length: 199

Content-Type: text/html; charset=utf-8 

3.给客户端响应JSON格式的数据

(1).处理器中的代码

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. //设置响应头中的内容类型
  3. w.Header().Set("Content-Type", "application/json")
  4. user := User{
  5. ID: 1,
  6. Username: "张三",
  7. Password: "123456",
  8. }
  9. //将user转换成JSON格式
  10. json, _ := json.Marshal(user)
  11. w.Write(json)
  12. }

(2).浏览器中的结果

{"ID":1, "Username":"张三","Password":"123456"}

(3).响应报文中的内容

HTTP/1.1 200 OK

Date:Fri, 10 Aug 2022 01:01:01 GMT

Content-Length: 199

Content-Type: application/json 

4.让客户端重定向

(1).处理器中的代码

  1. func handler(w http.ResponseWriter, r *http.Request) {
  2. //以下操作必须在WriteHeader之前进行
  3. w.Header().Set("Location", "https://www.baidu.com")
  4. w.WriteHeader(302)
  5. }

(2).响应报文中的内容

HTTP/1.1 200 OK

Location: https://www.baidu.com

Date:Fri, 10 Aug 2022 01:01:01 GMT

Content-Length:  0

Content-Type: text/plain; charset=utf-8

以上案例完整代码如下:

  1. package main
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "go_code/web_app/sql/model"
  6. "net/http"
  7. )
  8. //创建处理器函数
  9. func handler(w http.ResponseWriter, r *http.Request) {
  10. _, err := fmt.Fprintln(w, "请求地址:", r.URL.Path)
  11. _, err = fmt.Fprintln(w, "请求地址中后面的查询字符串:", r.URL.RawQuery)
  12. _, err = fmt.Fprintln(w, "请求地址中请求头数据:", r.Header)//返回map[string][][string]
  13. _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的信息:", r.Header["Accept-Encoding"]) //返回map切片
  14. _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的属性值:", r.Header.Get("Accept-Encoding"))//返回string
  15. 获取请求体中内容长度
  16. //len := r.ContentLength
  17. 创建byte切片
  18. //body := make([]byte, len)
  19. 将请求体中的内容读取到byte切片中
  20. //_, err = r.Body.Read(body)
  21. 浏览器中显示请求体中的内容
  22. //_, err = fmt.Fprintln(w, "浏览器中显示请求体中的内容:",string(body))
  23. //在解析表单r.Form之前,需执行以下方法
  24. //err = r.ParseForm()
  25. 如果form表单的action属性值的地址中也有与form表单参数名相同的请求参数,那么参数值都可以得到
  26. 并且form表单中的参数值会在url参数值的前面
  27. //_, err = fmt.Fprintln(w, "请求表单请求参数:", r.Form)
  28. //_, err = fmt.Fprintln(w, "post请求中Form表单请求参数:", r.PostForm)
  29. //
  30. //通过直接调用FOrmValue,PostFormValue直接获取请求参数中的值
  31. _, err = fmt.Fprintln(w, "Url中username请求参数的值:", r.FormValue("username"))
  32. _, err = fmt.Fprintln(w, "post请求中Form表单name请求参数:", r.PostFormValue("name"))
  33. if err != nil {
  34. fmt.Println(err)
  35. }
  36. }
  37. func handlerJson(w http.ResponseWriter, r *http.Request) {
  38. //设置响应内容头
  39. w.Header().Set("Content-Text", "application/json")
  40. //创建User
  41. user := &model.User{
  42. ID: 3,
  43. Name: "李四",
  44. Email: "lisi@qqlcom",
  45. }
  46. //转换成json格式
  47. data, _ := json.Marshal(user)
  48. //将json格式数据响应给客户端
  49. _, err := w.Write(data)
  50. if err != nil {
  51. fmt.Println(err)
  52. }
  53. }
  54. func handlerRedirect(w http.ResponseWriter, r *http.Request) {
  55. //设置响应头中的Location
  56. w.Header().Set("Location", "https://www.baidu.com")
  57. //设置响应状态码
  58. w.WriteHeader(302)
  59. }
  60. func main() {
  61. http.HandleFunc("/req", handler)
  62. http.HandleFunc("/res/json", handlerJson)
  63. http.HandleFunc("/res/redirect", handlerRedirect)
  64. err := http.ListenAndServe(":8080", nil)
  65. if err != nil {
  66. fmt.Println(err)
  67. }
  68. }

[上一节][golang Web开发] 2.golang web开发:操作数据库,增删改查,单元测试

[下一节][golang Web开发] 4.golang web开发:模板引擎 

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号