赞
踩
Go语音的net/http包提供了一系列用于表示HTTP报文的结构,可以使用它处理请求和发送响应,其中Request结构代表了客户端发送的请求报文,下面是Request讲解
- type Request struct {
- // Method指定HTTP方法(GET、POST、PUT等)。对客户端,""代表GET。
- Method string
- // URL在服务端表示被请求的URI,在客户端表示要访问的URL。
- //
- // 在服务端,URL字段是解析请求行的URI(保存在RequestURI字段)得到的,
- // 对大多数请求来说,除了Path和RawQuery之外的字段都是空字符串。
- // (参见RFC 2616, Section 5.1.2)
- //
- // 在客户端,URL的Host字段指定了要连接的服务器,
- // 而Request的Host字段(可选地)指定要发送的HTTP请求的Host头的值。
- URL *url.URL
- // 接收到的请求的协议版本。本包生产的Request总是使用HTTP/1.1
- Proto string // "HTTP/1.0"
- ProtoMajor int // 1
- ProtoMinor int // 0
- // Header字段用来表示HTTP请求的头域。如果头域(多行键值对格式)为:
- // accept-encoding: gzip, deflate
- // Accept-Language: en-us
- // Connection: keep-alive
- // 则:
- // Header = map[string][]string{
- // "Accept-Encoding": {"gzip, deflate"},
- // "Accept-Language": {"en-us"},
- // "Connection": {"keep-alive"},
- // }
- // HTTP规定头域的键名(头名)是大小写敏感的,请求的解析器通过规范化头域的键名来实现这点。
- // 在客户端的请求,可能会被自动添加或重写Header中的特定的头,参见Request.Write方法。
- Header Header
- // Body是请求的主体。
- //
- // 在客户端,如果Body是nil表示该请求没有主体买入GET请求。
- // Client的Transport字段会负责调用Body的Close方法。
- //
- // 在服务端,Body字段总是非nil的;但在没有主体时,读取Body会立刻返回EOF。
- // Server会关闭请求的主体,ServeHTTP处理器不需要关闭Body字段。
- Body io.ReadCloser
- // ContentLength记录相关内容的长度。
- // 如果为-1,表示长度未知,如果>=0,表示可以从Body字段读取ContentLength字节数据。
- // 在客户端,如果Body非nil而该字段为0,表示不知道Body的长度。
- ContentLength int64
- // TransferEncoding按从最外到最里的顺序列出传输编码,空切片表示"identity"编码。
- // 本字段一般会被忽略。当发送或接受请求时,会自动添加或移除"chunked"传输编码。
- TransferEncoding []string
- // Close在服务端指定是否在回复请求后关闭连接,在客户端指定是否在发送请求后关闭连接。
- Close bool
- // 在服务端,Host指定URL会在其上寻找资源的主机。
- // 根据RFC 2616,该值可以是Host头的值,或者URL自身提供的主机名。
- // Host的格式可以是"host:port"。
- //
- // 在客户端,请求的Host字段(可选地)用来重写请求的Host头。
- // 如过该字段为"",Request.Write方法会使用URL字段的Host。
- Host string
- // Form是解析好的表单数据,包括URL字段的query参数和POST或PUT的表单数据。
- // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
- Form url.Values
- // PostForm是解析好的POST或PUT的表单数据。
- // 本字段只有在调用ParseForm后才有效。在客户端,会忽略请求中的本字段而使用Body替代。
- PostForm url.Values
- // MultipartForm是解析好的多部件表单,包括上传的文件。
- // 本字段只有在调用ParseMultipartForm后才有效。
- // 在客户端,会忽略请求中的本字段而使用Body替代。
- MultipartForm *multipart.Form
- // Trailer指定了会在请求主体之后发送的额外的头域。
- //
- // 在服务端,Trailer字段必须初始化为只有trailer键,所有键都对应nil值。
- // (客户端会声明哪些trailer会发送)
- // 在处理器从Body读取时,不能使用本字段。
- // 在从Body的读取返回EOF后,Trailer字段会被更新完毕并包含非nil的值。
- // (如果客户端发送了这些键值对),此时才可以访问本字段。
- //
- // 在客户端,Trail必须初始化为一个包含将要发送的键值对的映射。(值可以是nil或其终值)
- // ContentLength字段必须是0或-1,以启用"chunked"传输编码发送请求。
- // 在开始发送请求后,Trailer可以在读取请求主体期间被修改,
- // 一旦请求主体返回EOF,调用者就不可再修改Trailer。
- //
- // 很少有HTTP客户端、服务端或代理支持HTTP trailer。
- Trailer Header
- // RemoteAddr允许HTTP服务器和其他软件记录该请求的来源地址,一般用于日志。
- // 本字段不是ReadRequest函数填写的,也没有定义格式。
- // 本包的HTTP服务器会在调用处理器之前设置RemoteAddr为"IP:port"格式的地址。
- // 客户端会忽略请求中的RemoteAddr字段。
- RemoteAddr string
- // RequestURI是被客户端发送到服务端的请求的请求行中未修改的请求URI
- // (参见RFC 2616, Section 5.1)
- // 一般应使用URI字段,在客户端设置请求的本字段会导致错误。
- RequestURI string
- // TLS字段允许HTTP服务器和其他软件记录接收到该请求的TLS连接的信息
- // 本字段不是ReadRequest函数填写的。
- // 对启用了TLS的连接,本包的HTTP服务器会在调用处理器之前设置TLS字段,否则将设TLS为nil。
- // 客户端会忽略请求中的TLS字段。
- TLS *tls.ConnectionState
- }
Request类型代表一个服务端接受到的或者客户端发送出去的HTTP请求。
Request各字段的意义和用途在服务端和客户端是不同的。除了字段本身上方文档,还可参见Request.Write方法和RoundTripper接口的文档
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方法
form.html提交如下:
- <form action="http://localhost:8080/getBody" method="POST">
- 用户名:<input type="text" name="username" value="admin"><br/>
- 密码:<input type="password" name="password" value="123456"><br/>
- <input type="提交">
- </form>
main.go获取客户端提交的信息,并解析如下:
- func handler(w http.ResponseWriter, r *http.Request) {
- //获取内容长度
- length := r.ContentLenth
- //创建一个切片
- body := make([]byte, length)
- //读取请求体
- r.Body.Read(body)
- fmt.Fprintln(w, "请求体中的内容是:", string(body))
- }
通过net/http库中的Request结构的字段以及方法来获取请求URL后面的请求参数以及form表单提交的请求参数
(1).类型是url.Values类型,Form是解析好的表单数据,包括URL字段的query参数和POST或PUT表单数据
包net/url下url.Values
(2).Form字段只有在调用Request的ParseForm后才有效,在客户端,会忽略请求中的本字段而使用Body替代
代码:
- func handler(w http.ResponseWriter, r *http.Request) {
- //解析表单
- r.ParseForm()
- //获取请求参数
- fmt.Fprintln(w, "请求参数:", r.Form)
- }
注意:在执行r.Form之前一定要调用r.ParseForm方法对表单进行解析
结果:请求参数为:map[password:[123456]] username:[admin]]
如果对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编码格式
代码如下:
- package main
-
- import (
- "fmt"
- "net/http"
- )
-
- //创建处理器函数
- func handler(w http.ResponseWriter, r *http.Request) {
- _, err := fmt.Fprintln(w, "请求地址:", r.URL.Path)
- _, err = fmt.Fprintln(w, "请求地址中后面的查询字符串:", r.URL.RawQuery)
- _, err = fmt.Fprintln(w, "请求地址中请求头数据:", r.Header)//返回map[string][][string]
- _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的信息:", r.Header["Accept-Encoding"]) //返回map切片
- _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的属性值:", r.Header.Get("Accept-Encoding"))//返回string
-
- 获取请求体中内容长度
- //len := r.ContentLength
- 创建byte切片
- //body := make([]byte, len)
- 将请求体中的内容读取到byte切片中
- //_, err = r.Body.Read(body)
- 浏览器中显示请求体中的内容
- //_, err = fmt.Fprintln(w, "浏览器中显示请求体中的内容:",string(body))
-
- //在解析表单r.Form之前,需执行以下方法
- //err = r.ParseForm()
- 如果form表单的action属性值的地址中也有与form表单参数名相同的请求参数,那么参数值都可以得到
- 并且form表单中的参数值会在url参数值的前面
- //_, err = fmt.Fprintln(w, "请求表单请求参数:", r.Form)
- //_, err = fmt.Fprintln(w, "post请求中Form表单请求参数:", r.PostForm)
-
-
- //通过直接调用FormValue,PostFormValue直接获取请求参数中的值
- _, err = fmt.Fprintln(w, "Url中username请求参数的值:", r.FormValue("username"))
- _, err = fmt.Fprintln(w, "post请求中Form表单name请求参数:", r.PostFormValue("name"))
- if err != nil {
- fmt.Println(err)
- }
- }
-
- func main() {
- http.HandleFunc("/req", handler)
- err := http.ListenAndServe(":8080", nil)
- if err != nil {
- fmt.Println(err)
- }
- }
- <html>
- <head>
- <meta charset="UTF-8">
- </head>
- </head>
- <body>
- <form action="http://127.0.0.1:8080/req?username=张三" method="post">
- 用户名:<input type="text" name="name"/><br/>
- 用密码:<input type="text" name="password"/><br/>
- <input type="submit">
- </form>
- </body>
- </html>
前面讲解了如何使用处理器中的*http.Request处理用户的请求,下面说一下如何使用http.ResponseWriter来给用户响应
(1).处理器中的代码
- func handler(w http.ResponseWriter, r *http.Request) {
- w.Write([]byte("你的请求服务端已收到"))
- }
(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
(1).处理器中的代码
- func handler(w http.ResponseWriter, r *http.Request) {
- html := `<html>
- <head>
- <title>测试响应内容为网页</title>
- <meta charset="utf-8"/>
- </head>
- <body>
- 以网页形式响应
- </body>
- </html>`
- w.Write([]byte(html))
- }
(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
(1).处理器中的代码
- func handler(w http.ResponseWriter, r *http.Request) {
- //设置响应头中的内容类型
- w.Header().Set("Content-Type", "application/json")
- user := User{
- ID: 1,
- Username: "张三",
- Password: "123456",
- }
- //将user转换成JSON格式
- json, _ := json.Marshal(user)
- w.Write(json)
- }
(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
(1).处理器中的代码
- func handler(w http.ResponseWriter, r *http.Request) {
- //以下操作必须在WriteHeader之前进行
- w.Header().Set("Location", "https://www.baidu.com")
- w.WriteHeader(302)
- }
(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
以上案例完整代码如下:
- package main
-
- import (
- "encoding/json"
- "fmt"
- "go_code/web_app/sql/model"
- "net/http"
- )
-
- //创建处理器函数
- func handler(w http.ResponseWriter, r *http.Request) {
- _, err := fmt.Fprintln(w, "请求地址:", r.URL.Path)
- _, err = fmt.Fprintln(w, "请求地址中后面的查询字符串:", r.URL.RawQuery)
- _, err = fmt.Fprintln(w, "请求地址中请求头数据:", r.Header)//返回map[string][][string]
- _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的信息:", r.Header["Accept-Encoding"]) //返回map切片
- _, err = fmt.Fprintln(w, "请求地址中请求头Accept-Encoding的属性值:", r.Header.Get("Accept-Encoding"))//返回string
-
- 获取请求体中内容长度
- //len := r.ContentLength
- 创建byte切片
- //body := make([]byte, len)
- 将请求体中的内容读取到byte切片中
- //_, err = r.Body.Read(body)
- 浏览器中显示请求体中的内容
- //_, err = fmt.Fprintln(w, "浏览器中显示请求体中的内容:",string(body))
-
- //在解析表单r.Form之前,需执行以下方法
- //err = r.ParseForm()
- 如果form表单的action属性值的地址中也有与form表单参数名相同的请求参数,那么参数值都可以得到
- 并且form表单中的参数值会在url参数值的前面
- //_, err = fmt.Fprintln(w, "请求表单请求参数:", r.Form)
- //_, err = fmt.Fprintln(w, "post请求中Form表单请求参数:", r.PostForm)
- //
-
- //通过直接调用FOrmValue,PostFormValue直接获取请求参数中的值
- _, err = fmt.Fprintln(w, "Url中username请求参数的值:", r.FormValue("username"))
- _, err = fmt.Fprintln(w, "post请求中Form表单name请求参数:", r.PostFormValue("name"))
- if err != nil {
- fmt.Println(err)
- }
- }
-
- func handlerJson(w http.ResponseWriter, r *http.Request) {
- //设置响应内容头
- w.Header().Set("Content-Text", "application/json")
- //创建User
- user := &model.User{
- ID: 3,
- Name: "李四",
- Email: "lisi@qqlcom",
- }
- //转换成json格式
- data, _ := json.Marshal(user)
- //将json格式数据响应给客户端
- _, err := w.Write(data)
- if err != nil {
- fmt.Println(err)
- }
- }
-
-
- func handlerRedirect(w http.ResponseWriter, r *http.Request) {
- //设置响应头中的Location
- w.Header().Set("Location", "https://www.baidu.com")
- //设置响应状态码
- w.WriteHeader(302)
- }
-
- func main() {
- http.HandleFunc("/req", handler)
- http.HandleFunc("/res/json", handlerJson)
- http.HandleFunc("/res/redirect", handlerRedirect)
- err := http.ListenAndServe(":8080", nil)
- if err != nil {
- fmt.Println(err)
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。