专栏首页cwl_JavaGO-处理请求

GO-处理请求

第 5 章:处理请求

Go 语言的 net/http 包提供了一系列用于表示 HTTP 报文的结构,我们可以使用它处理请求和发送相应,其中 Request 结构代表了客户端发送的请求报文,下面让我们看一下 Request 结构体

5.1 获取请求 URL

Request 结构中的 URL 字段用于表示请求行中包含的 URL,改字段是一个指向url.URL 结构的指针,让我们来看一下 URL 结构

  1. Path 字段、
  • 获取请求的 URL
  • 例如:http://localhost:8080/hello?username=admin&password=123456
    • 通过 r.URL.Path 只能得到 /hello
  1. RawQuery 字段
  • 获取请求的 URL 后面?后面的查询字符串\
  • 例如:http://localhost:8080/hello?username=admin&password=123456
    • 通过 r.URL.RawQuery 得到的是 username=admin&password=123456

5.2 获取请求头中的信息

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

  1. 获取请求头中的所有信息
  • r.Header
  • 得到的结果如下:
map[User-Agent:[Mozilla/5.0 (Windows NT 10.0; Win64; x64) 
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.62 Safari/537.36] 
Accept:[text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,ima
ge/apng,*/*;q=0.8] Accept-Encoding:[gzip, deflate, br] Accept-Language:[zhCN,zh;q=0.9,en-US;q=0.8,en;q=0.7] Connection:[keep-alive] Upgrade-InsecureRequests:[1]]
  1. 获取请求头中的某个具体属性的值,如获取 Accept-Encoding 的值
  • 方式一:r.Header[“Accept-Encoding”]
    • 得到的是一个字符串切片
    • 结果:[gzip, deflate, br]
  • 方式二:r.Header.Get(“Accept-Encoding”)
    • 得到的是字符串形式的值,多个值使用逗号分隔
    • 结果:gzip, deflate, br

5.3 获取请求体中的信息

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

  1. 由于 GET 请求没有请求体,所以我们需要在 HTML 页面中创建一个 form 表单,通过指定 method=”post”来发送一个 POST 请求 a) 表单
<form action="http://localhost:8080/getBody" method="POST">
	用户名:
	<input type="text" name="username" value="hanzong">
	<br/>
	密 码 :
	<input type="password" name="password" value="666666">
	<br/>
	<input type="submit">
</form>

b) 服务器处理请求的代码

package main
import (
	"fmt"
	"net/http"
)
func handler(w http.ResponseWriter, r * http.Request) {
	//获取内容的长度
	length: = r.ContentLength
	//创建一个字节切片
	body: = make([] byte, length)
	//读取请求体
	r.Body.Read(body)
	fmt.Fprintln(w, "请求体中的内容是:", string(body))
}
func main() {
	http.HandleFunc("/getBody", handler)
	http.ListenAndServe(":8080", nil)
}

c) 在浏览器上显示的结果

请求体中的内容是: username=hanzong&password=666666

5.4 获取请求参数

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

5.4.1 Form 字段

  1. 类型是 url.Values 类型,Form 是解析好的表单数据,包括 URL 字段的 query参数和 POST 或 PUT 的表单数据。
  1. Form 字段只有在调用 Request 的 ParseForm 方法后才有效。在客户端,会忽略请求中的本字段而使用 Body 替代
  1. 获取 5.3 中的表单中提交的请求参数(username 和 password)
  • 代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析表单
	r.ParseForm()
	//获取请求参数
	fmt.Fprintln(w, "请求参数为:", r.Form)
}

注意:在执行 r.Form 之前一定要调用 ParseForm 方法

  • 结果
请求参数为: map[password:[666666] username:[hanzong]]

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

<form action="http://localhost:8080/getBody?username=admin&pwd=123456"
method="POST">
	用 户 名 :
	<input type="text" name="username" value="hanzong">
	<br/>
	密码:
	<input type="password" name="password" value="666666">
	<br/>
	<input type="submit">
</form>
  • 则执行结果如下:
请求参数为:map[username:[hanzong admin] password:[666666] pwd:[123456]]
  • 我们发现:表单中的请求参数 username 和 URL 中的请求参数username 都获取到了,而且表单中的请求参数的值排在 URL 请求参数值的前面
  • 如果此时我们只想获取表单中的请求参数该怎么办呢?那就需要使用 Request 结构中的 PostForm 字段

5.4.2 PostForm 字段

  1. 类型也是 url.Values 类型,用来获取表单中的请求参数
  • 将 r.Form 改为 r.PostForm 之后的代码
func handler(w http.ResponseWriter, r * http.Request) {
	//解析表单
	r.ParseForm()
	//获取请求参数
	fmt.Fprintln(w, "请求参数为:", r.PostForm)
}
  • 结果
请求参数为: map[username:[hanzong] password:[666666]]
  1. 但是 PostForm 字段只支持 application/x-www-form-urlencoded 编码,如果form 表单的 enctype 属性值为 multipart/form-data,那么使用 PostForm 字段无法获取表单中的数据,此时需要使用 MultipartForm 字段
  • 说明:form 表单的 enctype 属性的默认值是 application/x-www-formurlencoded 编 码 , 实 现 上 传 文 件 时 需 要 讲 该 属 性 的 值 设 置 为multipart/form-data 编码格式
  1. FormValue 方法 a) 可以通过 FormValue 方法快速地获取某一个请求参数,该方法调用之前会自动调用 ParseMultipartForm 和 ParseForm 方法对表单进行解析
  • 代码
func handler(w http.ResponseWriter, r * http.Request) {
	//获取请求参数
	fmt.Fprintln(w, "请求参数username的值为:", r.FormValue("username"))
}
  • 结果
请求参数 username 的值为: hanzong
  1. PostFormValue 方法 a) 可以通过 PostFormValue 方法快速地获取表单中的某一个请求参数,该方法调用之前会自动调用 ParseMultipartForm 和 ParseForm 方法对表单进行解析
  • 代码
func handler(w http.ResponseWriter, r * http.Request) {
	//获取请求参数
	fmt.Fprintln(w, "请求参数 username 的值为:", r.PostFormValue("username"))
}
  • 结果:
请求参数 username 的值为: hanzong

5.4.4 MultipartForm 字段

为了取得 multipart/form-data 编码的表单数据,我们需要用到 Request 结构的ParseMultipartForm 方法和 MultipartForm 字段,我们通常上传文件时会将 form 表单的enctype 属性值设置为 multipart/form-data

  1. 表单
<form action="http://localhost:8080/upload" method="POST" enctype="multipart/form-data">
	用 户 名 :
	<input type="text" name="username" value="hanzong">
	<br/>
	文件:
	<input type="file" name="photo" />
	<br/>
	<input type="submit">
</form>
  1. 后台处理请求代码
package main
import (
	"fmt"
	"net/http"
)
func handler(w http.ResponseWriter, r * http.Request) {
	//解析表单
	r.ParseMultipartForm(1024)
	//打印表单数据
	fmt.Fprintln(w, r.MultipartForm)
}
func main() {
	http.HandleFunc("/upload", handler)
	http.ListenAndServe(":8080", nil)
}
  1. 浏览器显示结果
&{map[username:[hanzong]] map[photo:[0xc042126000]]}
  • 结果中有两个映射,第一个映射映射的是用户名;第二个映射的值是一个地址
  • MuiltipartForm 字段的类型为 *multipart.Form,multipart 包下 Form 结构的指针类型
  • 打开上传的文件
func handler(w http.ResponseWriter, r * http.Request) {
	//解析表单
	r.ParseMultipartForm(1024)
	fileHeader: = r.MultipartForm.File["photo"][0]
	file, err: = fileHeader.Open()
	if err == nil {
		data,
		err: = ioutil.ReadAll(file)
		if err == nil {
			fmt.Fprintln(w, string(data))
		}
	}
}
  1. FormFile 方法
  • net/http 提供的 FormFile 方法可以快速的获取被上传的文件,但是只能处理上传一个文件的情况。
  • 代码
func handler(w http.ResponseWriter, r * http.Request) {
	file,
	_,
	err: = r.FormFile("photo")
	if err == nil {
		data,
		err: = ioutil.ReadAll(file) if err == nil {
			fmt.Fprintln(w, string(data))
		}
	}
}

5.5 给客户端响应

前面我们一直说的是如何使用处理器中的 *http.Request 处理用户的请求,下面我们来说一下如何使用 http.ResponseWriter 来给用户响应

  1. 给客户端响应一个字符串
  • 处理器中的代码
func handler(w http.ResponseWriter, r * http.Request) {
	w.Write([] byte("你的请求我已经收到"))
}
  • 浏览器中的结果
你的请求我已经收到
  • 响应报文中的内容
HTTP/1.1 200 OK
Date: Fri, 10 Aug 2018 01:09:27 GMT
Content-Length: 27
Content-Type: text/plain; charset=utf-8
  1. 给客户端响应一个 HTML 页面
  2. 处理器中的代码
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))
}
  1. 浏览器中的结果
我是以网页的形式响应过来的!
  • 通过在浏览器中右键→查看网页代码发现确实是一个 html 页面
  1. 响应报文中的内容
HTTP/1.1 200 OK
Date: Fri, 10 Aug 2018 01:26:58 GMT
Content-Length: 194
Content-Type: text/html; charset=utf-8
  1. 给客户端响应 JSON 格式的数据
  2. 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//设置响应头中内容的类型
	w.Header().Set("Content-Type", "application/json")
	user: = User {
		ID: 1,
		Username: "admin",
		Password: "123456",
	}
	//将 user 转换为 json 格式
	json, _: = json.Marshal(user)
	w.Write(json)
}
  1. 浏览器中的结果
{"ID":1,"Username":"admin","Password":"123456"}
  1. 响应报文中的内容
HTTP/1.1 200 OK
Content-Type: application/json
Date: Fri, 10 Aug 2018 01:58:02 GMT
Content-Length: 47
  1. 让客户端重定向
  2. 处理器端代码
func handler(w http.ResponseWriter, r * http.Request) {
	//以下操作必须要在 WriteHeader 之前进行
	w.Header().Set("Location", "https:www.baidu.com")
	w.WriteHeader(302)
}
  1. 响应报文中的内容
HTTP/1.1 302 Found
Location: https:www.baidu.com
Date: Fri, 10 Aug 2018 01:45:04 GMT
Content-Length: 0
Content-Type: text/plain; charset=utf-8

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

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java基础-常用类(三)

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

    cwl_java
  • 前端基础-json-server与axios

    一个项目从立项开始,一般都是前后端同时进行编码工作的,而此时前端需要的接口和数据后台都是无法提供的;

    cwl_java
  • 经典笔试题-Web篇

    122、说出Servlet 的生命周期,并说出Servlet 和CGI 的区别? 【基础】 答:Web 容器加载Servlet 并将其实例化后,Servlet...

    cwl_java
  • $.ajax()

    天天_哥
  • CTF初识与深入

    这段时间一直在忙活CTF相关的东西,从参赛者到出题人,刷过一些题,也初步了解了出题人的逻辑;这篇文章就简单地讲一下CTF如何入门以及如何深入的学习、利用CTF这...

    信安之路
  • jQuery ajax - ajax() 方法jQuery ajax - ajax() 方法

    http://www.w3school.com.cn/jquery/ajax_ajax.asp

    一个会写诗的程序员
  • banner自动生成工具,ascii文字展示

    前言 post@ Ryan-Miao@github.io Spring-Boot里有个banner的功能,刚开始觉得很鸡肋。然而,现在觉得蛮有意思的。无聊的程序...

    Ryan-Miao
  • 异步加载的基本逻辑与浏览器抓包一般流程

    本篇内容不涉及任何R语言或者Python代码实现,仅从异步加载的逻辑实现过程以及浏览器抓包分析的角度来给大家分享一下个人近期学习爬虫的一些心得。 涉及到的工具有...

    数据小磨坊
  • CTF入门web篇18命令执行无回显的判断方法及dnslog相关例题

    命令执行可能会存在命令执行完没有回显,首先要判断命令是否有执行,可以通过三种方式来判断:延时、HTTP请求、DNS请求。

    牛油果
  • debian9 安装 Python3.

    如果提示-bash: aptitude: command not found,则表示你的机子需要安装aptitude,安装命令如下:

    py3study

扫码关注云+社区

领取腾讯云代金券