Go(2)

前言

填坑…

Go 有一点比较要注意的就是大小写了。


先填一下 Go 语法的坑吧

把简单的手册读完,就这样吧,我读的是菜鸟教程 2333。


请求

请求 URL

request.URL 是个指向 url.URL 结构体的指针,而这个结构体中存放了请求 URL 相关的数据,比如 request.URL.Path。

请求头

请求头存放在 request.Header 中,遍历输出一下:

1
2
3
for key, value := range request.Header {
fmt.Fprintln(writer, key, ": ", value)
}

大致就是这样:

1
2
3
4
5
6
7
Accept :  [text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8]
Accept-Language : [zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3]
Accept-Encoding : [gzip, deflate]
Cookie : [csrftoken=J8qkz9Ebk6VDXsf4T1rkTmJ39T3uiOe0zD4j4tm9xOe5K8ATfcioJPu7sQKBGTPO]
Connection : [keep-alive]
Upgrade-Insecure-Requests : [1]
User-Agent : [Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:56.0) Gecko/20100101 Firefox/56.0]

可以使用 [] 或者 Get() 来取值。

请求 body

body 存放在 request.Body 中,是一个 io.ReadCloser 接口,同时包含了 reader 和 closer 接口,我们要获取 body 的内容就需要调用 Read 方法,从 Body 中读取固定长度的字节放进 make 方法创建的语言切片中:

1
2
3
4
5
len := request.ContentLength
body := make([]byte, len)
request.Body.Read(body)
request.Body.Close()
fmt.Fprintf(writer, string(body))

可以得到未经过处理的 body,想要获取 POST 提交的表单还有内置的方法,不需要我们自己去解析。

Form
1
2
3
request.ParseForm()
fmt.Fprintln(writer, request.Form["a"][0], request.Form["a"][1])
fmt.Fprintln(writer, request.Form)

这种方式不分 GET 和 POST,比如我们 GET 提交 a=456&a=0,POST 提交 a=123&b=abc&a=789,结果就是:

1
2
123 789
map[a:[123 789 456 0] b:[abc]]

它会将相同键值的合并到一起。

PostForm

如果只想要 POST 的数据,就使用 PostForm 方法:

1
2
request.ParseForm()
fmt.Fprintln(writer, request.PostForm)

结果就只剩下:

1
map[a:[123 789] b:[abc]]
MultipartForm

这种类型要自己写太麻烦了,所以整个简单的表单:

1
2
3
4
5
<form action="http://***/register?myname=Twings" method="POST" enctype="multipart/form-data">
<input type="text" name="myname">
<input type="file" name="myfile">
<input type="submit">
</form>

然后:

1
2
request.ParseMultipartForm(1024)
fmt.Fprintln(writer, request.MultipartForm)

效果如下:

1
&{map[myname:[Aluvion]] map[myfile:[0xc00007e820]]}

MultipartForm 只会接受 POST 的数据,而且文本数据和文件会分开放在两个 map 里面。

FormValue、Form、PostFormValue、PostForm

比较方便的取值方法,会自动解析 multipart/form-data 或者 application/x-www-form-urlencoded 编码方式的表单(1.12 版本的 go 中好像可以自动解析了)使用方式类似这样:

1
fmt.Fprintln(writer, request.FormValue("myname"))

Form 会返回整个表单,而 FormValue 只会按顺序返回一个我们需要的键的值。

Form 会从 GET 和 POST 中取值。

感觉上来说,GET 的数据用多路复用器来获取比较好,POST 的可以用 PostFormValue。

文件上传

要引入 io/io.util 包,比较麻烦的方法,不过可以上传多个文件:

1
2
3
4
5
6
7
8
9
request.ParseMultipartForm(1024)
fileHeader := request.MultipartForm.File["myfile"][0]
file, err := fileHeader.Open()
if err == nil {
data, err := ioutil.ReadAll(file)
if err == nil {
fmt.Fprintln(writer, string(data))
}
}

比较简单的方法,类似 FormValue:

1
2
3
4
5
6
7
file, _, err := request.FormFile("myfile")
if err == nil {
data, err := ioutil.ReadAll(file)
if err == nil {
fmt.Fprintln(writer, string(data))
}
}

获取完文件还有别的操作,比如大小校验、文件类型校验、写入文件等等,这里留坑。

JSON

留个坑。

响应

ResponseWriter 接口有三种用来处理响应的方法:

  • Write 用于处理响应主体
  • WriteHeader 用于处理响应码
  • Header 用于处理响应头

如下:

1
2
3
4
5
6
7
8
9
10
11
html := `<html>
<head>
<title>Hello World!</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>`
writer.Header().Set("FLAG", "flag{0123456789}")
writer.WriteHeader(233)
writer.Write([]byte(html))

响应主体还是使用模板比较好一点啊。

Go 的 Cookie 是一个结构体,要设置 Cookie 可以先设置一个 http.Cookie 结构体,然后用 Header 将 Cookie Set 到客户端:

1
2
3
4
5
6
7
8
9
10
11
12
cookie1 := http.Cookie{
Name: "HINT1",
Value: "FLAG is not here",
HttpOnly: true,
}
cookie2 := http.Cookie{
Name: "HINT2",
Value: "FLAG in db",
HttpOnly: true,
}
writer.Header().Set("Set-Cookie", cookie1.String())
writer.Header().Add("Set-Cookie", cookie2.String())

用 Add 可以继续第二个 Cookie,或者使用 http.SetCookie:

1
2
http.SetCookie(writer, &cookie1)
http.SetCookie(writer, &cookie2)

Go 想要获取 Cookie 也有两种方法,一种是简单粗暴的 Header[“Cookie”] / Header.Get(“Cookie”),另一种比较简单,是内置的 request.Cookie 方法,Cookies 方法获得的则是全部 Cookie,得到的是一个指向 Cookie 结构体的指针:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
cookie1, err := request.Cookie("HINT1")
if err != nil {
if err == http.ErrNoCookie {
fmt.Fprintln(writer, "No Cookie1")
}
} else {
cookie2, err := request.Cookie("HINT2")
if err != nil {
if err == http.ErrNoCookie {
fmt.Fprintln(writer, "No Cookie2")
}
} else {
html := cookie1.Value + cookie2.Value
writer.Write([]byte(html))
}
}

Go 的 base64 编码,引入 encoding/base64 包,然后:

1
base64.URLEncoding.EncodeToString()

Orz