WEB 开发之如何获取请求数据

如何获取请求数据

我们先写一个 server 端的的 http 服务器,输出当前时间

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"

    // 绑定处理器函数
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

执行

请求:

通过 Fiddler 工具我们可以看到我们现在请求体里面没有任何数据

响应

我们现在看一下响应,当前响应只有时间

现在我们来看一下请求行,请求头里面的数据因该如何来获取

5.1 获取请求行中数据

请求行中有请求的方式,请求的 Url , 请求的协议,我们都知道请求的对象会分装到 http.Request 对象中,所以我们自然的看 http.Request 对象,我们所有请求的信息都在 http.request 里面

通过 go doc http.Request 对象,发现他是一个 结构体

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 获取请求方法
        fmt.Println("method:", r.Method)

        // 获取 url
        fmt.Println("url:", r.URL)

        // 获取 http 协议版本
        fmt.Println("http protocol:", r.Proto)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

只要我们一访问该服务,我们就会在终端看到我们的请求行的数据

终端输出

5.2 获取请求头中数据

我们通过go doc http.request.header 查看得知 header 是一个 header 自定义类型,由此我们知道请求的数据是一个 key/value 的结构体,所以 header 应该是一个 map 结构

[05:53:19 root@go web]#go doc http.header
package http // import "net/http"

type Header map[string][]string
    A Header represents the key-value pairs in an HTTP header.

    The keys should be in canonical form, as returned by CanonicalHeaderKey.

func (h Header) Add(key, value string)
func (h Header) Clone() Header
func (h Header) Del(key string)
func (h Header) Get(key string) string
func (h Header) Set(key, value string)
func (h Header) Values(key string) []string
func (h Header) Write(w io.Writer) error
func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 因为我们知道了 r.Header 是一个 map 类型,所以我们 for-range 遍历获取请求头数据
        for k, v := range r.Header {
            fmt.Println(k, v)
        }

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

只要我们一访问该服务,我们就会在终端看到我们的请求行的数据

通过输出的信息,我们发现 header 是一个 map 类型

[05:56:14 root@go web]#go run main.go 
Upgrade-Insecure-Requests [1]
User-Agent [Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70]
Accept [text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9]
Accept-Encoding [gzip, deflate]
Accept-Language [zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6]
Connection [keep-alive]
Cache-Control [max-age=0]
Pragma [no-cache]
User-Agent [Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70]
Referer [http://10.0.0.10:8888/]
Accept-Encoding [gzip, deflate]
Connection [keep-alive]
Cache-Control [no-cache]
Accept [image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8]
Accept-Language [zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6]

5.2.1 获取单个请求头部数据

现在我们想获取单个请求头部信息,我们就可以通过 http.header 的方法进行获取

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 通过 GET 方法获取请求头的 User-Agent 信息
        header := r.Header

        // header.Get("User-Agent") 这里传入的 User-Agent 是 key 字段,然后输出对应的 value
        // 获取其他头部信息也是通过 Get 方法
        fmt.Println(header.Get("User-Agent"))

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

只要我们一访问该服务,我们就会在终端看到我们的请求行的数据

终端输出

[06:00:57 root@go web]#go run main.go 
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 Edg/91.0.864.70

而且我们想要什么头部信息,他都能拿到

5.3 获取请求体中数据

我们都知道请求体如果发起 POST 方法的话可以提交数据。我们通过 http.request.body 可以获取请求体信息

http.request.body 是一个接口类型,实现了 reader 和 closer 方法,所以 body 也能读,就可以通过 io.Copy 方法将他输出到终端上

[06:25:55 root@go web]#go doc http.request.body
package http // import "net/http"

type Request struct {
    // Body is the request's body.
    // 
    // For client requests, a nil body means the request has no body, such as a GET
    // request. The HTTP Client's Transport is responsible for calling the Close
    // method.
    // 
    // For server requests, the Request Body is always non-nil but will return EOF
    // immediately when no body is present. The Server will close the request body.
    // The ServeHTTP Handler does not need to.
    // 
    // Body must allow Read to be called concurrently with Close. In particular,
    // calling Close should unblock a Read waiting for input.
    Body io.ReadCloser

    // ... other fields elided ...
}

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 通过 io.Copy 将请求体数据输出
        // os.Stdout 输出到终端上
        // r.Body 获取请求体信息
        io.Copy(os.Stdout, r.Body)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

启动程序,之后我们通过 curl 浏览器对该程序提交一次 post 请求

[06:30:30 root@go ~]#curl -XPOST "127.0.0.1:8888/post" -d"a=curl&b=post请求"
2021-07-21 06:32:08

# -XPOST:请求方法是 post
# 127.0.0.1:8888/post:访问本机的 post 路径
# -d"a=curl&b=post请求":提交的请求数据
# 2021-07-21 06:32:08 服务器端 web 数据

curl 请求之后程序终端就会输出我们的请求数据

[06:30:54 root@go web]#go run main.go 
a=curl&b=post请求

5.3.1 获取自定义提交参数

在请求的时候任意的东西都能作为参数给服务器端进行提交

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 获取请求的 Token 字段
        header := r.Header
        // 输出 token 字段的值
        fmt.Println(header.Get("Token"))

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))
    })
    http.ListenAndServe(addr, nil)
}

启动程序,客户端 curl 访问

[06:32:08 root@go ~]#curl -XPOST "127.0.0.1:8888/post" -H "Token:zz"
2021-07-21 06:53:06

# -H "Token:zz" 请求头部信息 token 字段值为 zz

请求之后服务器响应

[06:52:20 root@go web]#go run main.go 
zz

# 输出 token 字段的值

5.4 获取提交数据

http 请求里面任意一个文本,任意一块的内容都可以作为数据进行提交,但是我们常用的一个传递数据的方式都有哪些呢,但是我们常用的数据提交呢还有一些固定的使用方式,我们来看一下

提交方式:

  • 第一种提交方式,在 URl 中传递数据,在访问的时候 url?加参数名字=他的值

    • url?argname1=argvalue1&argname2=argvalue2

  • 第二种提交方式,通过 body 提交数据

    • body 中数据格式,常用的有 3 种

      • application/x-www-form-urlencoded

      • application/json

      • multipart/form-data

      • 自定义格式

5.4.1 第一种方式获取数据

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        /*
         提交方式:
         第一种提交方式
        */
        fmt.Println(r.URL)

        // 服务器端响应时间给客户端
        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

启动程序浏览器访问,并且我们在访问的时候添加参数信息

http://10.0.0.10:8888/?z=1&g=2

此时服务器端会在终端输出提交信息

[07:09:37 root@go webrequest]#go run main.go 
/?z=1&g=2

但是我们如何将这些获取到的数据提取出来呢?在 go 中的话已经有函数帮我们实现了该功能

[07:12:54 root@go webrequest]#go doc http.request.form
package http // import "net/http"

type Request struct {
    // Form contains the parsed form data, including both the URL field's query
    // parameters and the PATCH, POST, or PUT form data. This field is only
    // available after ParseForm is called. The HTTP client ignores Form and uses
    // Body instead.
    Form url.Values     # 类型为 url.Values    

    // ... other fields elided ...
}

# 查看 url.Values 类型
[07:13:04 root@go webrequest]#go doc url.values
package url // import "net/url"

type Values map[string][]string # key 为 string value 为 [] string
    Values maps a string key to a list of values. It is typically used for query
    parameters and form values. Unlike in the http.Header map, the keys in a
    Values map are case-sensitive.

func ParseQuery(query string) (Values, error)
func (v Values) Add(key, value string)
func (v Values) Del(key string)
func (v Values) Encode() string
func (v Values) Get(key string) string
func (v Values) Set(key, value string)

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        /*
         提交方式:
         第一种提交方式
        */

        // 解析 url 参数
        r.ParseForm()
        // 获取提交数据信息,接收的参数类型都是 string
        fmt.Println(r.Form)
        
        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })
    http.ListenAndServe(addr, nil)
}

运行程序,浏览器访问时添加参数

http://10.0.0.10:8888/?z=1&g=2

此时启动程序终端就会输出一个 map 类型信息

[07:15:56 root@go webrequest]#go run main.go 
map[g:[2] z:[1]]

# 也就是我们的 url 中添加的参数

5.4.1.1 获取提交数据的单个参数信息

http://10.0.0.10:8888/?z=1&g=2 我们在这个 url 中可以看到有 z 和 g 两个 key 的参数,那我们如何获取一个单独参数的数据呢,通过http.Request.Form.Get 方法获取

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        /*
         提交方式:
         第一种提交方式
        */

        // 解析 url 参数
        r.ParseForm()
        // 获取提交数据信息,接收的参数类型都是 string
        fmt.Println(r.Form)
        // 获取单独参数的数据信息的第一个参数值
        fmt.Println(r.Form.Get("z"))

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

浏览器访问

程序终端输出

[07:27:17 root@go webrequest]#go run main.go 
map[g:[2] z:[1]]
1

# 把 z=1 的 value 进行输出

但是当我们在 url 中一个参数传递了多个值我们该如何获取呢?如这样的一个 url 请求 http://10.0.0.10:8888/?z=1&g=2&z=3,通过 http.Request.Form["参数key"]

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        /*
         提交方式:
         第一种提交方式
        */

        // 解析 url 参数
        r.ParseForm()
        // 获取提交数据信息,接收的参数类型都是 string
        fmt.Println(r.Form)
        // 获取参数的多个数据值
        fmt.Println(r.Form["z"])

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

启动程序浏览器访问

程序终端输出 z 的多个参数

[07:34:47 root@go webrequest]#go run main.go 
map[g:[2] z:[1 3]]
[1 3]

5.4.2对 body application/x-www-form-urlencoded 类型数据获取

代码这块其实没有过多变化,还是使用的和 第一种方式一样的代码,只不过我们的请求方式变为了 POST

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {
        // 获取请求方法
        fmt.Println("method:", r.Method)
        // 解析 url 参数
        r.ParseForm()
        // 获取提交数据信息,接收的参数类型都是 string
        fmt.Println(r.Form)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

通过 curl 浏览器发起一个 POST 请求

[06:53:06 root@go ~]#curl -XPOST "127.0.0.1:8888/post" -d"a=curl&b=post请求"

程序输出

[07:47:47 root@go webrequest]#go run main.go 
method: POST                 # 请求方式 post
map[a:[curl] b:[post请求]]    # 获取 body 数据

所以我们可以通过 http.Request.Form 不仅可以获取 RUL 参数也能获取 body 中的参数

5.4.3 当 url 和 body 种传入数据重复,如何只获取 body 数据

因为 http.Request.Form 会同时获取 url 和 body 中的数据,但是我们就像获取 body 数据该如何获取呢?

通过http.Request.PostForm ,而且和 Form 一样在使用之前都需要解析

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 获取请求方法
        fmt.Println("method:", r.Method)
        // 解析 url 参数
        r.ParseForm()

        // 获取
        fmt.Println("form 获取", r.Form)
        fmt.Println("post form 获取", r.PostForm)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

启动程序,浏览器访问

[08:01:47 root@go ~]#curl -XPOST "127.0.0.1:8888/post?a=111" -d"a=curl&b=post请求"
2021-07-21 08:02:24

程序输出结果,就可以通过 post form 这种方式来指定获取 body 中参数

[08:02:19 root@go webrequest]#go run main.go 
method: POST
form 获取 map[a:[curl 111] b:[post请求]]
post form 获取 map[a:[curl] b:[post请求]]

5.4.4 对 body application/json 类型进行解析

对自定义类型需要获取到 body 原始数据,使用特定解码器就OK了,如果我们提交的时 json 格式,使用 json 的解码器就成功了,我们在通过对自定义类型进行解析的时候不适用 http.request.parseForm 进行解析,而是通过我们自己定义的解析器进行解析

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 获取请求方法
        fmt.Println("method:", r.Method)
        // 自定义解析器
        // 对 body 请求体进行解析
        io.Copy(os.Stdout, r.Body)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

启动服务,并通过 curl 浏览器进行访问,传递 json 格式

[08:37:42 root@go ~]#curl -XPOST "127.0.0.1:8888/post?a=111" -d"{\"x\":1}"
2021-07-21 08:37:46

# -d"{\"x\":1}" 传递 json 格式的 body

服务器拿到 json 格式

[08:36:20 root@go webrequest]#go run main.go 
method: POST
{"x":1}

这个时候服务器端已经有了 body 信息了,就可以通过 json.Unmarshal() 进行解析并且拿到对应的数据

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

func main() {
    addr := ":8888"
    http.HandleFunc("/", func(rw http.ResponseWriter, r *http.Request) {

        // 获取请求方法
        fmt.Println("method:", r.Method)

        // 自定义解析器,并且将读取到的数据是一个 []byte 赋值给 ctx
        ctx, err := ioutil.ReadAll(r.Body)
        if err != nil {
            fmt.Println(err)
            return
        }

        // 定义 map 来接收解析后的数据
        v := make(map[string]interface{})

        // 解析,传递 v 的指针进行赋值
        json.Unmarshal(ctx, &v)

        // 此时的 v 就是我们传递过来的 json 请求数据
        fmt.Println(v)

        fmt.Fprint(rw, time.Now().Format("2006-01-02 15:04:05"))

    })

    http.ListenAndServe(addr, nil)
}

启动服务,并通过 curl 浏览器进行访问,传递 json 格式

[08:37:42 root@go ~]#curl -XPOST "127.0.0.1:8888/post?a=111" -d"{\"x\":1}"
2021-07-21 08:37:46

# -d"{\"x\":1}" 传递 json 格式的 body

服务器拿到 json 格式

[08:50:10 root@go webrequest]#go run main.go 
method: POST
map[x:1]                    # 拿到请求的 json 数据

如果是 xml 类型的提交数据我们把解析类型改为 xml 进行解析,如果是自定义类型我们就通过自定义类型解析

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇