专栏首页学院君的专栏Go 语言 Web 编程系列(十三)—— 获取用户请求数据(上)

Go 语言 Web 编程系列(十三)—— 获取用户请求数据(上)

0、GET/POST 请求数据

在 PHP 中,可以直接通过全局变量 $_GET$_POST 快速获取 GET/POST 请求数据,GET 请求数据主要是 URL 查询字符串中包含的参数,以前面在线论坛项目的群组详情页为例:

http://localhost:8080/thread/read?id=adb02107-d7c3-4f27-4de4-b586f231380e

上述 URL 请求中的 id=adb02107-d7c3-4f27-4de4-b586f231380e 就属于 GET 请求数据,也就是查询字符串,而对于用户登录表单:

当用户输入注册邮箱和密码后点击「登录」按钮,会将输入框中的数据作为请求实体发送 POST 请求到服务端,执行登录认证,这里的表单数据就是 POST 请求数据,如果我们查看表单请求路由的 HTTP 报文:

就会看到表单请求数据,也就是上文提到的 HTTP 请求实体。

当然根据对服务端资源的操作类型不同,可以进一步细分为 POST、PUT、PATCH、DELETE 等包含请求实体的请求方法,为了简化模型,我们这里只讨论 POST 请求,而且其他几种请求方法也可以统一通过 POST 请求完成,通常只有在设计遵循 RESTful 风格的 API 接口时,才会严格划分不同的请求方法,关于这一点,后面介绍 REST + JSON 接口时再详细讨论。

1、Form

Go 语言中获取用户请求数据的方式要更复杂一些,Go 也为此提供多个不同的结构体帮助我们读取不同请求类型的数据,首先,我们可以通过请求对象上的 Form 读取所有 GET/POST 请求数据,在 handlers/post.go 中新增 EditPost 方法如下:

func EditPost(w http.ResponseWriter, r *http.Request)  {
    r.ParseForm()
    fmt.Fprintln(w, r.Form)
}

需要注意的是,在通过 r.Form 获取所有请求数据之前,必须要先通过 r.ParseForm() 解析所有请求数据,否则无法获取数据。

routes/web.go 中新增一个路由:

WebRoute{
    "UpdatePost",
    "POST",
    "/post/edit",
    handlers.EditPost,
},

然后我们重启 HTTP 服务器,在命令行通过 curl 发起一个包含不同类型请求数据的请求:

可以看到,打印结果表明 r.Form 是一个包含所有请求数据的字典类型(map),包含 URL 查询字符串和 POST 请求数据,这是一个 POST 请求,如果是 GET 请求呢?我们为 EditPost 定义一个 GET 请求路由:

WebRoute{
    "EditPost",
    "GET",
    "/post/edit",
    handlers.EditPost,
},

再重启 HTTP 服务器,发起一个只包含查询字符串的 GET 请求(默认是 GET 请求,不需要通过 -X GET 指定):

同样可以打印出和 POST 请求完全一样的结果。

因此,request 对象上的 Form 可以获取所有请求参数,包括查询字符串和请求实体,并且不限请求类型。如果你想要进一步要获取指定的参数值,可以以索引方式获取指定参数对应的值,也可以通过 Form 提供的 Get 方法,就像我们从一个普通字典类型获取键值一样:

id1 := r.Form["id"]
id2 := r.Form.Get("id")
fmt.Println(id1)
fmt.Println(id2)

只不过两者的返回值类型不一样,前者是一个字符串切片,后者是一个字符串值:

[1]
1

2、PostForm

上面的结果同时返回了查询字符串和请求实体,如果只想获取请求实体(即 POST 表单中的数据),可以通过 PostForm 实现:

func EditPost(w http.ResponseWriter, r *http.Request)  {
    r.ParseForm()
    id := r.Form.Get("id")
    fmt.Println("post id:", id)
    fmt.Println("form data:", r.PostForm)
    io.WriteString(w, "表单提交成功")
}

这样一来,就只能获取到 POST 数据了:

然后在 HTTP 服务器日志,可以看到如下答应结果:

可以看到 r.PostForm 返回的也是字典类型数据,数据格式和 r.Form 完全一致,并且这次只包含了 POST 表单请求数据,不包含 URL 查询字符串,也就是说,通过 r.PostForm 只能获取 POST 请求数据(请求实体数据),无法获取 GET 请求数据(查询字符串中的数据),你可以再次发起 GET 请求进行验证:

这个时候,可以看到服务器打印日志之包含 id 信息,表单信息为空:

通过 PostForm 获取具体参数值的方式和 Form 一样,这里就不再赘述了:

title := r.PostForm.Get("title")
content := r.PostForm.Get("content")

实际上,我们在前面的在线论坛项目中,就是通过这个方式获取表单请求数据的:

err := request.ParseForm()
user, err := models.UserByEmail(request.PostFormValue("email"))

3、FormValue/PostFormValue

最后,还可以通过 FormValuePostFormValue 获取用户请求数据,使用它们的好处是不再需要单独调用 ParseForm 对表单数据进行解析,不过使用这两个方法的时候只能获取特定请求数据,不能一次获取所有请求数据:

func EditPost(w http.ResponseWriter, r *http.Request)  {
    fmt.Println("post id:", r.FormValue("id"))
    fmt.Println("post title:", r.PostFormValue("title"))
    fmt.Println("post title:", r.PostFormValue("content"))
    io.WriteString(w, "表单提交成功")
}

FormValue/PostFormValue 的区别和 Form/PostForm 一样,这里通过命名就可以看出来,前者可以获取所有 GET/POST 请求数据(即查询字符串和请求实体),后者只能获取 POST 请求实体数据。

注:FormValue/PostFormValue 之所以不用显式调用 ParseForm 解析请求数据,是因为底层对其进行了封装,实际上还是要调用这个方法。

4、获取 JSON 请求数据

上面的示例默认都是基于 HTML 表单请求,对于客户端提交的 JSON 格式数据,使用 ParseForm 是无法解析并获取数据的,因为 HTML 表单请求数据默认是通过 application/x-www-form-urlencoded 编码的,而 JSON 请求数据通常是通过 application/json 编码,ParseForm 只能解析通过 application/x-www-form-urlencoded 编码的数据。

对于 JSON 请求数据的解析,目前我们可以通过上篇教程介绍的,读取完整请求实体并进行 JSON 解码实现,下面我们改写 AddPost 方法实现如下:

type Post struct {
    Title string `json:"title"`
    Content string `json:"content"`
}

func AddPost(w http.ResponseWriter, r *http.Request)  {
    len := r.ContentLength   // 获取请求实体长度
    body := make([]byte, len)  // 创建存放请求实体的字节切片
    r.Body.Read(body)        // 调用 Read 方法读取请求实体并将返回内容存放到上面创建的字节切片
    // io.WriteString(w, string(body))
    post := Post{}
    json.Unmarshal(body, &post)  // 对读取的 JSON 数据进行解析
    fmt.Fprintf(w, "%#v\n", post)   // 格式化输出结果
}

我们将读取的请求实体数据通过 JSON 解码映射到 Post 结构体对象并将其输出到响应结果。

重启 HTTP 服务器,通过 curl 模拟客户端提交 JSON 请求数据:

我们通过 -H 选项指定请求数据编码格式为 application/json,然后请求数据调整为 JSON 格式字符串,最后通过输出结果可以看到在服务端 JSON 请求数据已经可以成功解析并获取。

5、小结

到这里,我们已经了解了在 Go Web 编程中,常见的用户请求数据如何解析并获取(URL 查询字符串、POST 表单数据、JSON 请求数据),实际上,和 PHP 中的 $_GET$_POST 类似,Go 也是将 HTTP 请求数据映射到请求对象对应的结构体,然后开发者可以从上下文请求对象中解析并读取这些请求数据,使用这些封装好的对象的好处是它们屏蔽了底层的细节,统一了数据格式,可以大大提高开发效率,减少不必要的数据格式兼容成本。

本文分享自微信公众号 - 学院君的后花园(geekacademy),作者:学院君

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-20

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Go 语言错误及异常处理篇(三):panic 和 recover

    前面学院君介绍了 Go 语言通过 error 接口统一进行错误处理,但这些错误都是我们在编写代码时就已经预见并返回的,对于某些运行时错误,比如数组越界、除数为0...

    学院君
  • Go 语言 Web 编程系列(十二)—— 通过 Request 读取 HTTP 请求报文

    在前面的教程中,学院君给大家介绍了 Go 语言中 HTTP 服务器的实现和内置的路由分发实现,以及第三方的路由器解决方案 —— gorilla/mux,接下来,...

    学院君
  • Go 语言 Web 编程系列(一)—— 快速入门:创建第一个 Web 应用

    首先,我们基于 HTTP 编程中介绍的 net/http 包来实现一个简单的 HTTP 服务器作为 Web 服务器:

    学院君
  • 老生常谈GET和POST,以备常查

    ------------------- GET 和 POST 请求的区别 // --TCP/IP 协议详解卷3 13.3.1 报文类型:请求与响应 HTTP ...

    小端
  • axios发送两次请求及遇到的坑

    在以前的Vue项目商城中,使用axios组件做数据请求,在页面交互中发现会发送两次网络请求,因为没有造成什么影响,一直也没有追究原因。在一个新的项目中,需要加入...

    honey缘木鱼
  • 从http规范角度来看xmlhttprequest发送请求

    最近有点怠工,停更好久,今天分享一篇小白文,原生ajax,看标题肯定不同于其他文章的ajax,而是从http规范角度来看xmlhttprequest发送请求。

    挥刀北上
  • Python接口测试实战1(下)- 接口

    Fiddler主界面 Fiddler的主界面分为 工具面板、会话面板、监控面板、状态面板

    py3study
  • 文加图, 理解Http请求与响应

    在讲解OkHttp之前, 我们首先来个高清大图, 看下http请求的整个步骤, 有个整体概念.

    望天
  • CORS跨域资源共享(一):模拟跨域请求以及结果分析,理解同源策略【享学Spring MVC】

    版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 ...

    YourBatman
  • 测试工具Fiddler(三)—— 常见功能介绍

    3、autoresponder: 文件代理:可以使用fiddler来自定义请求返回的文件。

    小菠萝测试笔记

扫码关注云+社区

领取腾讯云代金券