Skip to content

GO - HTTP

写在前面

GO关于HTTP的用法

处理请求

  http.DefaultServeMux作为一个handler(goroutine),处理http请求

http.ListenAndServer()

  • 第一个参数是网络地址
  • 第二个参数是handler。如果为nil,那么就使用DefaultServeMux

http.Server

  这是一个struct,第一个字段表示网络地址,第二个字段表示Handler,有个ListenAndServe方法。

创建http Server

go
	// 更灵活的写法
	server := http.Server{
		Addr:    "localhost:8080",
		Handler: nil,
	}
	server.ListenAndServe()

	// 和上面功能一样
	http.ListenAndServe("localhost:8080", nil)

Handler

  一个接口,上面定义了ServerHTTP方法,还有一个指向Request这个Struct的指针。 Handler的struct

go
type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

DefaultServeMux

  DefaultServeMux是一个multiplexer,可以认为是路由器。其实际上也是一个Handler。

http.Handle(pattern string, handler Handler)

  如果调用http.Handle()方法,实际上调用的是DefaultServeMux上的Handle方法。DefaultServeMux就是ServerMux的指针变量。   pattern其实是入口地址,后面是处理的方法。

http.HandleFunc(pattern string, handler func(ResponseWriter, *Request))

  Handler函数是指那些行为与handler类似的函数。接受一个http.ResponseWriter和一个指向http.Request的指针。   总结一下,就是和Handle方法差不多,区别在于给了一个和ServeHTTP函数声明一样的函数,在调用HandleFunc后会将这个函数转换为带有这个方法的Handler,然后就和Handle没什么大区别了。

五大handler

  • NotFoundHandler
  • RedirectHandler(url string, code int)
  • StripPrefix(prefix string, h handler):用来去除指定前缀
  • TimeoutHandler(h Handler, dt time.Duration, msg string)
  • FileServer():等于是在指定路径开了个假的文件系统,可以通过url路径访问指定文件

HTTP Request和HTTP Response

  两者具有相同的结构:

  • 请求(响应)行
  • 0或多个Header
  • 空行
  • 可选的消息体(Body)

  Request是个struct,代表客户端发送的HTTP请求消息。   重要的字段有:

  • URL
  • Header
  • Body
  • Form、PostForm、MultipartForm

  可以通过Request的方法获取请求中的Cookie、URL、User Agent等等。

url.URL

  也是一个struct。

URL Fragment

  如果是从浏览器发出的请求,由于浏览器会把fragment去掉,我们无法提取出Fragment字段的值。 alt text

Request Header

  是一个map,用来表述HTTP Header里的Key-Value对。Header map的key是string类型,value是[]string,设置Key的时候会创建一个空的[]string作为Value,vallue第一个元素就是新header的值。可以使用append对key添加一个新的header值。

  • 使用r.Header返回一个map
  • 使用r.Header["Acccept-Encoding"]返回[]string类型
  • 使用r.Header.Get("Acccept-Encoding")返回string类型

Request.Body

  • Body是一个io.ReadCloser接口,有一个Reader接口和一个Closer接口
    • Reader接口定义了Open方法,参数为[]byte,返回byte数量和可选的错误
    • Closer接口定义了Close方法,没有参数
go
func (m *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	length := r.ContentLength
	body := make([]byte, length)
	r.Body.Read(body)
	fmt.Fprintln(w, string(body))
}

URL Query

  使用r.URL.RawQuery可以获取实际查询的原始字符串,使用r.URL.Query()会提供查询字符串对应的map[string][]string

处理表单

POST

  通过POST发的数据对格式可以通过表单的Content Type来指定,也就是enctype属性。 enctype:

  • 默认是application/x-www-form-urlencoded

  浏览器会将表单数据编码到查询字符串里面。简单文本

  • multipart/form-data

  每一个name-value对都会被转换为MIME消息部分。大量数据,比如上传文件

  • text/plain(H5支持)

GET

  没有请求Body,所有数据都需要通过URL的name-value对来发送。

FORM字段

  • Form 包括URL和Body数据
  • PostForm 不包括URL数据,只包括Body数据,仅支持application/x-www-form-urlencoded
  • MultiForm 支持multipart/form-data,使用ParseMultipartForm返回一个struct而不是map

  multipart/form-data用来上传文件,从File字段获得FileHeader,调用其Open方法来获得文件。可以使用ioutil.ReadAll函数把文件内容读取到byte切片里面。

ResponseWriter

  • 从服务器向客户端返回响应需要使用ResponseWriter
  • ResponseWriter是一个接口,handler用它来返回响应
  • 真正支撑ResponseWriter的幕后struct是非导出的http.response

为什么ResponseWriter传参的时候不是指针?

  ResponseWriter实际上是一个interface,它代表了一个指针。

go
// A response represents the server side of an HTTP response.
type response struct {
	conn             *conn
	req              *Request // request for this response
	reqBody          io.ReadCloser
	cancelCtx        context.CancelFunc // when ServeHTTP exits
	wroteHeader      bool               // a non-1xx header has been (logically) written
	wroteContinue    bool               // 100 Continue response was written
	wants10KeepAlive bool               // HTTP/1.0 w/ Connection "keep-alive"
	wantsClose       bool               // HTTP request has Connection "close"
  ...

  如上,response指针实现了ResponseWriter接口的所有方法,因此它可以被调用进函数里面。

Write

  用来写入Http的body里面,如果header里面没有设定content type,则数据的前512字节会被用来检测。

WriteHeader

  接受一个整数类型作为参数,作为HTTP响应的状态码。如果该方法没有显式调用,那么在第一次调用Write方法前,会隐式调用该方法,设定为200。调用该方法后就无法再修改header里面的东西了

  返回headers的map,可以进行修改,修改后的headers体现在返回客户端的HTTP响应里面。写在WriteHeader之前!!!

内置的Response

  • NotFound函数,包括404和错误信息
  • ServeFile函数,从文件系统提供文件
  • ServeContent,可以把实现了io.ReadSeeker接口的任何东西里面的内容返回给请求者。
  • Redirect函数

模版引擎

  感觉像快速开发前端页面。

html
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  {{ . }}
</body>
</html>

  如上,在html文件中使用该占位符。

go
func process(w http.ResponseWriter, r *http.Request) {
  t, _ := template.ParseFiles("tmpl.html")
  t.Execute(w, "Hello World!")
}

  实际上,ParseFiles既是函数也是方法,除了上述写法,还可以使用template.new("tmpl.html")先创建模版,再调用模版上的ParseFiles("tmpl.html")方法即可。

Controller层

  把不同的请求送到不同的controller中进行处理。

JSON(TAGS)

  可以定义go的struct转换成json后的格式。

类型映射:

gojson
boolboolean
float64数值
stringstrings
nilnull
go
type Company struct {
	ID      int    `json:"id"`
	Name    string `json:"name"`
	Country string `json:"country"`
}

  如果不知道具体的结构,可以使用map[string]interface{}存储任意json对象,可以用[]interface{}存储任意json数组。

中间件

  请求和响应时都会经过中间键进行处理。功能:

  • 记录日志
  • 保证安全
  • 处理请求超时问题
  • 压缩响应

Request Context

func(*Request) Context() context.Context会返回当前请求的上下文 func(*Request) WithContext(ctx context.Context) context.Context会基于Context进行修改,实际上创建一个新的Context。

go
type Context interface {
	Deadline() (deadline time.Time, ok bool)
	Done() <-chan struct{}
	Err() error //平常为nil,如果Done了则记录原因
	Value(key interface{}) interface{}
}

  上面的值都是用于读取,而不能进行设置,想进行更改只能创建新的context

  • WithCancel(),它有一个 CancelFunc
  • WithDeadline(),带有一个时间戳(time.Time)
  • WithTimeout(),带有一个具体的时间段(time.Duration)
  • WithValue(),在里面可以添加一些值