• 《Go Web 编程》之第4章 处理请求


    第4章 处理请求

    4.1 请求和响应

    HTTP报文在客户端和服务器间传递消息,分为HTTP请求和HTTP响应,结构如下:

    1)请求行或响应行;
    (2)零或多个首部;
    (3)一个空行;
    (4)可选报文主体。
    
    • 1
    • 2
    • 3
    • 4
    GET /Protocols/rfc2616/rfc2616.html HTTP/1.1
    Host: www.w3.org
    User-Agent: Mozilla/5.0
    (empty line)
    
    • 1
    • 2
    • 3
    • 4

    4.1.1 Request结构

    type Request struct {
    	Method string
    	
    	URL *url.URL
    	
    	Proto      string // "HTTP/1.0"
    	ProtoMajor int    // 1
    	ProtoMinor int    // 0
    	
    	Header Header
    	Body io.ReadCloser
    	
    	GetBody func() (io.ReadCloser, error)
    	ContentLength int64
    	TransferEncoding []string
    	Close bool
    	Host string
    	
    	Form url.Values
    	PostForm url.Values
    	MultipartForm *multipart.Form
    	
    	Trailer Header
    	RemoteAddr string
    	RequestURI string
    	TLS *tls.ConnectionState
    	Cancel <-chan struct{}
    	Response *Response
    	ctx context.Context
    }
    
    • 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

    4.1.2 请求URL

    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)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    //URL一般格式:
    scheme://[userinfo@]host/path[?query][#fragment]
    //解析为:
    scheme:opaque[?query][#fragment]
    
    • 1
    • 2
    • 3
    • 4
    http://www.example.com/post?id=123&thread_id=456
    //RawQuery为
    id=123&thread_id=456
    
    • 1
    • 2
    • 3

    浏览器向服务器发送请求时会剔除URL中的片段部分,服务器接收到URL中无Fragment字段。

    4.1.3 请求首部

    type Header map[string][]string
    
    • 1

    添加、删除、获取和设置。

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func headers(w http.ResponseWriter, r *http.Request) {
    	h := r.Header
    	//h := r.Header["Accept-Encoding"]
    	//h := r.Header.Get("Accept-Encoding")
    	fmt.Fprintln(w, h)
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/headers", headers)
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    4.1.4 请求主体

    Body io.ReadCloser
    
    • 1

    GET请求无报文主体。

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func body(w http.ResponseWriter, r *http.Request) {
    	len := r.ContentLength
    	body := make([]byte, len)
    	r.Body.Read(body)
    	fmt.Fprintln(w, string(body))
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/body", body)
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    curl -id "first_name=sausheong&last_name=chang" 127.0.0.1:8080/body
    
    • 1

    4.2 Go与HTML表单

    POST请求基本通过HTML表单发送:

    <form action="/process" method="post">
    	<input type="text" name="first_name"/>
    	<input type="text" name="last_name"/>
    	<input type="submit"/>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    表单输入数据以键值形式记录在请求主体中。
    决定POST请求发送键值对时格式的HTML表单内容类型(content type),由表单的enctype属性(默认"application/x-www-form-urlencoded")指定。

    <form action="/process" method="post" enctype="application/x-www-form-urlencoded">
    	<input type="text" name="first_name"/>
    	<input type="text" name="last_name"/>
    	<input type="submit"/>
    </form>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    浏览器至少支持application/x-www-form-urlencoded和multipart/form-data,HTML5还支持text/plain。

    • application/x-www-form-urlencoded
      &分隔的键值对保存在请求主体中。
    • multipart/form-data
      表单数据转换为MIME报文,键值对带有各自内容类型和内容配置(disposition)。
      传送大量数据(如上传文件),可Base64编码,以文本方式传送二进制数据。

    HTML表单可发送GET请求,键值对在请求URL中,无主体。

    4.2.1 Form字段

    URL、主体数据提取到Form、PostForm和MultipartForm字段中。

    Request获取表单数据的步骤:
    (1)ParseFomr或者ParseMultipartForm方法,对请求进行语法分析;
    (2)访问Form、PostForm或者MultipartForm字段。

    client.html

    <html>
      <head>    
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Go Web Programming</title>
      </head>
      <body>
        <form action="http://127.0.0.1:8080/process?hello=world&thread=123" method="post" enctype="application/x-www-form-urlencoded">
          <input type="text" name="hello" value="sau sheong"/>
          <input type="text" name="post" value="456"/>
          <input type="submit"/>
        </form>
      </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func process(w http.ResponseWriter, r *http.Request) {
    	r.ParseForm()
    	fmt.Fprintln(w, r.Form)
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/process", process)
    	server.ListenAndServe()
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    //URL
    http://127.0.0.1:8080/process?hello=world&thread=123
    //post主体
    hello=sau sheong&post=456
    
    //输出
    //r.ParseForm()和r.Form包含url和表单键值对
    //且表单值总是排在URL值的前面
    map[thread:[123] hello:[sau sheong world] post:[456]]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4.2.2 PostForm字段

    //r.ParseForm()和r.PostForm(只支持application/x-www-form-urlencoded编码)
    //只包含表单键值对
    
    • 1
    • 2
    //r.ParseForm()和r.Form(multipart/form-data)
    //只返回URL查询值
    
    • 1
    • 2

    4.2.3 MultipartForm字段

    获取multipart/form-data编码的表单数据,需要ParseMultipartForm方法(需要时会自行调用ParseFrom方法)和MultipartForm字段。

    //从multipart编码表单里取出字节数
    r.ParseMultipartForm(1024)
    
    //&{map[hello:[sau sheong] post:[456]] map[]}
    //只包含表单键值对
    //第二个空映射map[]用来记录用户上传的文件
    fmt.Fprintln(w, r.MultipartForm)
    
    
    //FormValue只取第一个值
    //需要时自动调用ParseFrom或ParseMultipartForm
    fmt.Fprintln(w, r.FormValue("hello"))
    
    
    //PostFormValue只返回表单值
    //需要时自动调用ParseFrom或ParseMultipartForm
    fmt.Fprintln(w, r.PostFormValue("hello"))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    FormValue和PostFormValue解析multipart/form-data表单,无结果。

    整理:
    (1)
    表单(application/x-www-form-urlencoded,默认)
    +
    ParseFrom方法
    +
    Form字段
    👇
    URL键值对+表单键值对;

    (2)
    表单(application/x-www-form-urlencoded,默认)
    +
    ParseFrom方法
    +
    PostForm字段
    👇
    表单键值对;

    (3)
    表单(multipart/form-data)
    +
    ParseMultipartFrom方法
    +
    MultipartFrom字段
    👇
    表单键值对;

    (4)
    表单(application/x-www-form-urlencoded,默认)
    +
    FromValue字段
    👇
    URL键值对+表单键值对;

    (5)
    表单(application/x-www-form-urlencoded,默认)
    +
    PostFromValue字段
    👇
    表单键值对;

    4.2.4 文件

    multipart/form-data编码常用于文件上传,需要file类型的input标签。

    <html>
      <head>    
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title>Go Web Programming</title>
      </head>
      <body>
        <form action="http://localhost:8080/process?hello=world&thread=123" method="post" enctype="multipart/form-data">
          <input type="text" name="hello" value="sau sheong"/>
          <input type="text" name="post" value="456"/>
          <input type="file" name="uploaded">
          <input type="submit">
        </form>
      </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    )
    
    func process(w http.ResponseWriter, r *http.Request) {
    	r.ParseMultipartForm(1024)
    	fileHeader := r.MultipartForm.File["uploaded"][0]
    	file, err := fileHeader.Open()
    	if err == nil {
    		data, err := ioutil.ReadAll(file)
    		if err == nil {
    			fmt.Fprintln(w, string(data))
    		}
    	}
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/process", process)
    	server.ListenAndServe()
    }
    
    • 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
    package main
    
    import (
    	"fmt"
    	"io/ioutil"
    	"net/http"
    )
    
    func process(w http.ResponseWriter, r *http.Request) {
    	file, _, err := r.FormFile("uploaded")
    	if err == nil {
    		data, err := ioutil.ReadAll(file)
    		if err == nil {
    			fmt.Fprintln(w, string(data))
    		}
    	}
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/process", process)
    	server.ListenAndServe()
    }
    
    • 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

    4.2.5 处理含JSON主体的POST请求

    不同客户端使用不同方式编码POST请求。

    • jQuery使用POST+默认表单编码+首部Content-Type: application/x-www-form-urlencoded
    • Angular使用POST+表单编码application/json

    ParseForm方法不接受application/json编码。

    4.3 ResponseWriter

    处理器通过ResponseWriter接口创建HTTP响应。
    ResponseWriter接口内部会使用http.response结构(非导出,nonexported)。

    4.3.1 Write方法

    字节数组作为参数,写入响应主体。
    首部未设置内容类型时,通过写入前512字节决定。

    package main
    
    import (
    	"fmt"
    	"encoding/json"
    	"net/http"
    )
    
    type Post struct {
    	User    string
    	Threads []string
    }
    
    func writeExample(w http.ResponseWriter, r *http.Request) {
    	str := `
    Go Web Programming
    

    Hello World

    `
    w.Write([]byte(str)) } func main() { server := http.Server{ Addr: "127.0.0.1:8080", } http.HandleFunc("/write", writeExample) server.ListenAndServe() }
    • 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
    curl -i 127.0.0.1:8080/writer
    
    • 1

    4.3.2 WriteHeader方法

    响应状态码,未设置时默认返回200 OK。

    package main
    
    import (
    	"fmt"
    	"encoding/json"
    	"net/http"
    )
    
    type Post struct {
    	User    string
    	Threads []string
    }
    
    func writeHeaderExample(w http.ResponseWriter, r *http.Request) {
    	w.WriteHeader(501)
    	fmt.Fprintln(w, "No such service, try next door")
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/writeheader", writeHeaderExample)
    	server.ListenAndServe()
    }
    
    • 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
    curl -i 127.0.0.1:8080/writerheader
    
    • 1

    4.3.2 Header方法

    写入首部映射。
    先调用Header方法写入首部,再调用WriteHeader(执行后,不允许修改首部)写入状态码。

    package main
    
    import (
    	"fmt"
    	"encoding/json"
    	"net/http"
    )
    
    type Post struct {
    	User    string
    	Threads []string
    }
    
    func headerExample(w http.ResponseWriter, r *http.Request) {
    	w.Header().Set("Location", "http://google.com")
    	w.WriteHeader(302)
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/redirect", headerExample)
    	server.ListenAndServe()
    }
    
    • 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
    curl -i 127.0.0.1:8080/redirect
    
    • 1

    4.3.4 完整JSON响应

    package main
    
    import (
    	"fmt"
    	"encoding/json"
    	"net/http"
    )
    
    type Post struct {
    	User    string
    	Threads []string
    }
    
    func writeExample(w http.ResponseWriter, r *http.Request) {
    	str := `
    Go Web Programming
    

    Hello World

    `
    w.Write([]byte(str)) } func writeHeaderExample(w http.ResponseWriter, r *http.Request) { w.WriteHeader(501) fmt.Fprintln(w, "No such service, try next door") } func headerExample(w http.ResponseWriter, r *http.Request) { w.Header().Set("Location", "http://google.com") w.WriteHeader(302) } func jsonExample(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") post := &Post{ User: "Sau Sheong", Threads: []string{"first", "second", "third"}, } json, _ := json.Marshal(post) w.Write(json) } func main() { server := http.Server{ Addr: "127.0.0.1:8080", } http.HandleFunc("/write", writeExample) http.HandleFunc("/writeheader", writeHeaderExample) http.HandleFunc("/redirect", headerExample) http.HandleFunc("/json", jsonExample) server.ListenAndServe() }
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    curl -i 127.0.0.1:8080/json
    
    • 1

    4.4 cookie

    存储在客户端的、体积较小的信息,最初通过服务器HTTP响应报文发送(set-cookie),之后客户端每次请求发送cookie。
    cookie划分为会话cookie和持久cookie。

    4.4.1Go与cookie

    type Cookie struct {
    	Name  string
    	Value string
    
    	Path       string
    	Domain     string
    	Expires    time.Time
    	RawExpires string 
    
    	MaxAge   int
    	Secure   bool
    	HttpOnly bool
    	SameSite SameSite
    	Raw      string
    	Unparsed []string
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    会话cookie或临时cookie(未设置Expires字段),浏览器关闭时自动移除。
    持久cookie(设置了Expires字段),时间过期或手动删除。
    Expires绝对时间,几乎所有浏览器都支持。
    MaxAge相对时间,HTTP 1.1推荐使用。

    4.4.2 发送cookie至浏览器

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func setCookie(w http.ResponseWriter, r *http.Request) {
    	c1 := http.Cookie{
    		Name:     "first_cookie",
    		Value:    "Go Web Programming",
    		HttpOnly: true,
    	}
    	c2 := http.Cookie{
    		Name:     "second_cookie",
    		Value:    "Manning Publications Co",
    		HttpOnly: true,
    	}
    	w.Header().Set("Set-Cookie", c1.String())
    	w.Header().Add("Set-Cookie", c2.String())
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/set_cookie", setCookie)
    	server.ListenAndServe()
    }
    
    • 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
    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func setCookie(w http.ResponseWriter, r *http.Request) {
    	c1 := http.Cookie{
    		Name:     "first_cookie",
    		Value:    "Go Web Programming",
    		HttpOnly: true,
    	}
    	c2 := http.Cookie{
    		Name:     "second_cookie",
    		Value:    "Manning Publications Co",
    		HttpOnly: true,
    	}
    	http.SetCookie(w, &c1)
    	http.SetCookie(w, &c2)
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/set_cookie", setCookie)
    	server.ListenAndServe()
    }
    
    • 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

    4.4.3 从浏览器获取cookie

    package main
    
    import (
    	"fmt"
    	"net/http"
    )
    
    func setCookie1(w http.ResponseWriter, r *http.Request) {
    	c1 := http.Cookie{
    		Name:  "first_cookie",
    		Value: "Go Programming",
    	}
    	c2 := http.Cookie{
    		Name:     "second_cookie",
    		Value:    "Go Web Programming",
    		HttpOnly: true,
    	}
    	w.Header().Set("Set-Cookie", c1.String())
    	w.Header().Add("Set-Cookie", c2.String())
    	fmt.Fprintf(w, "%s\n%s\n", c1.String(), c2.String())
    }
    
    func setCookie2(w http.ResponseWriter, r *http.Request) {
    	c1 := http.Cookie{
    		Name:  "first_cookie",
    		Value: "Go Programming",
    	}
    	c2 := http.Cookie{
    		Name:  "second_cookie",
    		Value: "Go Web Programming",
    		HttpOnly: true,
    	}
    	http.SetCookie(w, &c1)
    	http.SetCookie(w, &c2)
    }
    
    func getCookie1(w http.ResponseWriter, r *http.Request) {
    	cookie := r.Header["Cookie"]
        fmt.Fprintf(w, "%s\n", cookie)
    }
    
    func getCookie2(w http.ResponseWriter, r *http.Request) {
    	cookie, err := r.Cookie("first_cookie")
    	if err != nil {
    		fmt.Fprintln(w, "Cannot get Cookie")
    	}
    	cookies := r.Cookies()
    	fmt.Fprintf(w, "%s\n%s\n", cookie, cookies)
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
    	http.HandleFunc("/set_cookie", setCookie2)
    	http.HandleFunc("/get_cookie", getCookie2)
    	server.ListenAndServe()
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    //获取的Set-Cookie保存在a.cookie文件中
    curl -i -c a.cookie http://127.0.0.1:8080/set_cookie
    
    //发送a.cookie文件中的cookies
    curl -i -b a.cookie http://127.0.0.1:8080/get_cookie
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.4.4 cookie实现闪现消息

    某个条件满足时,页面上显示临时消息(闪现消息,flash message),刷新页面后消失。

    package main
    
    import (
        "encoding/base64"
    	"fmt"
    	"net/http"
        "time"
    )
    
    func set_message(w http.ResponseWriter, r *http.Request) {
        msg := []byte("Hello World")
        cookie := http.Cookie{
            Name:  "flash",
    		//响应首部对空格,百分号,中文等特殊字符的URL编码要求
            Value: base64.URLEncoding.EncodeToString(msg),
        }
        http.SetCookie(w, &cookie)
    }
    
    func show_message(w http.ResponseWriter, r *http.Request) {
        cookie, err := r.Cookie("flash")
        if err != nil {
            if err == http.ErrNoCookie {
                fmt.Fprintln(w, "no messages to show")
            }
        } else {
            expire_cookie := http.Cookie{
                Name:    "flash",
    				MaxAge:   -1,              //负值
    				Expires:  time.Unix(1, 0), //过去时间
            }
    		
    		//MaxAge设置负值,Expires设置过去时间
    		//SetCookie将同名cookie("flash")发送到客户端
    		//等价于完全移除这个cookie
            http.SetCookie(w, &expire_cookie)
    		
            value, _ := base64.URLEncoding.DecodeString(cookie.Value)
            fmt.Fprintln(w, string(value))
        }
    }
    
    func main() {
    	server := http.Server{
    		Addr: "127.0.0.1:8080",
    	}
        http.HandleFunc("/set_message", set_message)
        http.HandleFunc("/show_message", show_message)
    	server.ListenAndServe()
    }
    
    • 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
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    curl -i -c b.cookie http://127.0.0.1:8080/set_message
    curl -i -b b.cookie -c b.cookie http://127.0.0.1:8080/show_message
    
    
    curl -i -b b.cookie -c b.cookie http://127.0.0.1:8080/show_message
    
    • 1
    • 2
    • 3
    • 4
    • 5
  • 相关阅读:
    Leetcode.670 最大交换 中等
    深入理解 Spring 的事件机制
    css实现鼠标多样化
    【校招VIP】java语言考点之异常
    C#.NET体系图文概述—2024最全总结
    云安全之身份认证与授权机制介绍
    基于SSM框架的管理系统-计算机毕设 附源码 23402
    力扣刷题之移动零
    学习pytorch10 神经网络-最大池化的作用
    JAVA烟草经销平台的设计与实现计算机毕业设计Mybatis+系统+数据库+调试部署
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/126737443