Go-Web编程

Go-Web编程读书笔记

第一章

HTTP请求

请求行

请求方法

请求方法请求行中的第一个单词,它指明了客户端想要对资源执行的操作。HTTP 0.9 只有GET一个方法,HTTP 1.0 添加了POST方法和HEAD方法,而HTTP 1.1则添加了PUT、DELETE、OPTIONS、TRACE和CONNECT这5个方法,并允许开发者自行添加更多方法

image-20220604203544591

幂等性

image-20220604203712772

说白了就是再次请求会不会对服务器产生影响,Post一般用于数据的新增,所以发起二次请求,服务器可能还会新增这条数据,而Put Delete不会,因为发起二次请求修改和删除的还是原来的那条数据

请求头

image-20220604204044255

请求报文

image-20220604204210735

响应状态码

image-20220604204232850

响应头

image-20220604204300287

快速开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
package main

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

func main() {
//定义了路径为/的处理函数
http.HandleFunc("/", handler)
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Println(err)
}
}
//处理请求的函数
func handler(writer http.ResponseWriter, request *http.Request) {
//将request.URL.Path[1:]拼接到Hello World %s !的%s中,并写入到响应流里
fmt.Fprintf(writer, "Hello World %s !", request.URL.Path[1:])
}

使用多路复用创建Web服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func main() {
mux := http.NewServeMux()
_ = http.FileServer(http.Dir("/public"))
mux.HandleFunc("/", index)
server := &http.Server{
Addr: "0.0.0.0:8080",
Handler: mux,
}
err := server.ListenAndServe()
if err != nil {
log.Println("[[Main]] Server start failed")
}
}

func index(writer http.ResponseWriter, request *http.Request) {
writer.Write([]byte("哈哈哈哈哈"))
}

请求的接收与处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
func authenticate(w http.ResponseWriter, r *http.Request) {
//解析post参数
err := r.ParseForm()
if err != nil {
log.Println("[[authenticate]] Request ParseForm failed err:", err)
}
email := r.PostFormValue("email")
fmt.Println("我是接收到的参数", email)
//创建session
//session:=user.CreateSession
//设置cookie
cookie := http.Cookie{
Name: "_cookie",
Value: "session.uuid",
HttpOnly: true,
}
//将创建的cookie写入到ResponseWriter里
http.SetCookie(w, &cookie)
//重定向
http.Redirect(w, r, "/", http.StatusFound)
}

func parseSession(w http.ResponseWriter, r *http.Request) {
err := r.ParseForm()
if err != nil {
log.Println("[[parseSession]] Request ParseForm failed err:", err)
}
cookie,err := r.Cookie("_cookie")
if err != nil {
log.Println("[[parseSession]] Get Cookie failed err:", err)
}
uuid := cookie.Value
fmt.Println("uuid is ",uuid)
}

Web应用的基本构成

image-20220604215135411

再细分一下

image-20220604215311229

实际上ServeMux就是起到了一个Url映射的作用,将对应的Url请求映射到指定的Handler处理器上

接收请求的规则

如果请求一个不存在的路径,或者有个/hello的处理器,请求/hello/there,多路复用器最终会找不到路径,而退而求其次的将请求交给根处理器,也就是url为 / 的处理器

![image-20220604215735482](/Users/zwj/Library/Application Support/typora-user-images/image-20220604215735482.png)

处理请求

请求

Request结构

请求URl

image-20220604220004535

1
2
3
4
5
6
7
8
9
10
11
12
type URL struct {
Scheme string
Opaque string // encoded opaque data
User *Userinfo // username and password information
Host string // host or host:port
Path string // path (relative paths may omit leading slash)
RawPath string // encoded path hint (see EscapedPath method)
ForceQuery bool // append a query ('?') even if RawQuery is empty
RawQuery string // encoded query values, without '?'
Fragment string // fragment for references, without '#'
RawFragment string // encoded fragment hint (see EscapedFragment method)
}

scheme :opaque [?query] [#fragment]
在开发Web应用的时候,我们常常会让客户端通过URL的查询参数向服务器传递信息,而URL结构的RawQuery字段记录的就是客户端向服务器传递的查询参数字符串。举个例子,如果客户端向地址http://www.example com/post?id=123&thread_ id=456发送一个请求,那么RawQuery字段的值就会被设置为id=123&thread_ id=456 虽然通过对RawQuery字段的值进行语法分析可以获取到键值对格式的查询参数,但直接使用Request结构的Form字段来获取这些键值对会更方便一些。本章稍后就会对Request结构的Form字段、PostForm 字段和MultipartForm字段进行介绍。另外需要注意的一点是,如果请求报文是由浏览器发送的,那么程序将无法通过URL结构的Fragment字段获取URL的片段部分。本书在第1章中就提到过,浏览器在向服务器发送请求之前,会将URL中的片段部分剔除掉因为服务器接收到的都是不包含片段部分的URL,所以程序自然也无法通过Fragment字段去获取URL的片段部分了,造成这个问题的原因在于浏览器,与我们正在使用的net/http 库无关。URL结构的Fragment字段之所以会存在,是因为并非所有请求都来自浏览器:除了浏览器发送的请求之外,服务器还可能会接收到HTTP客户端库、Angular这样的客户端框架或者某些其他工具发送的请求;此外别忘了,不仅服务器程序可以使用Request结构,客户端库也同样可以把Request结构用作自己的一部分。

请求头

实际上请求头就是一个map结构

image-20220604220614447

获取请求头
1
2
3
4
5
6
func index(writer http.ResponseWriter, request *http.Request) {
h := request.Header
AcceptEncoding := h.Get("Accept_Encoding")
fmt.Fprintln(writer, h)
fmt.Fprintln(writer, AcceptEncoding)
}

请求主体

1
2
3
4
5
6
func body(writer http.ResponseWriter, request *http.Request) {
len := request.ContentLength
body := make([]byte, len)
request.Body.Read(body)
fmt.Fprintln(writer, string(body))
}

image-20220604221259042

解析Form字段

可以解析form也可以解析x-www-form-urlencoded

1
2
3
4
5
6
func form(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id := r.Form.Get("id")
fmt.Fprintln(w, r.Form)
fmt.Fprintln(w, "id is ", id)
}

image-20220604223257756

form-data 和 x-www-form-urlencoded的区别

  • multipart/form-data:可以上传文件或者键值对,最后都会转化为一条消息
  • x-www-form-urlencoded:只能上传键值对,而且键值对都是通过&间隔分开的

PostForm字段

只能解析x-www-form-urlencoded类型的

1
2
3
4
5
6
func postForm(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
id := r.Form.Get("id")
fmt.Fprintln(w, r.Form)
fmt.Fprintln(w, "id is ", id)
}

image-20220604225017269

multipartForm字段

image-20220604225409227

1
2
3
4
func multipartForm(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(1000)
fmt.Fprintln(w, r.MultipartForm)
}

对比

image-20220604225456058

文件

image-20220604225951465

image-20220604225932739

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
func file(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(1024)
fileHeader := r.MultipartForm.File["file"][0]
file, _ := fileHeader.Open()
NewFile, _ := os.Create("file.proto")
bytes := make([]byte, 1024)
for {
n, err := file.Read(bytes)
NewFile.Write(bytes[:n])
if err != nil {
if err == io.EOF {
break
}
}
}
NewFile.Close()
fmt.Fprintln(w, r.MultipartForm)
}

json数据

1
2
3
4
5
6
7
8
9
10
11
12
13
func fromJson(w http.ResponseWriter, r *http.Request) {
//接收json并解析
var person person
_ = json2.NewDecoder(r.Body).Decode(&person)
bytes, _ := json2.Marshal(&person)
w.Header().Set("Content-Type", "application/json")
w.Write(bytes)
}

type person struct {
Name string `json:"name"`
Age int `json:"age"`
}

image-20220604230803529

获取cookie
1
2
3
4
func getCookie(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("cookie")
fmt.Fprintln(w, cookie)
}

image-20220604231621274

设置cookie
1
2
3
4
5
6
7
8
9
func setCookie(w http.ResponseWriter, r *http.Request) {
cookie := r.Header.Get("cookie")
c := http.Cookie{
Name: "cookieInfo",
Value: cookie,
HttpOnly: true,
}
http.SetCookie(w, &c)
}

image-20220604231934493

响应

测试

单元测试

基准测试

image-20220605215922841

1
go test -v -cover -short -bench

指定运行测试的次数

1
go test -run x -bench

并发

并发与并行

image-20220605220258111

image-20220605220309638


Go-Web编程
https://www.gravity.wang/2022/06/05/go-web/
Author
Gravity
Posted on
June 5, 2022
Licensed under