命令记不住

登陆

Web 编程基础 http 响应

Web编程基础http响应

如何响应客户端的请求。最简单的方式是通过 http.ResponseWriter 发送字符串给客户端。
但是这种方式仅限于发送字符串

1.ResponseWriter

ResponseWriter

func (w http.ResponseWriter, r *http.Request)

这里的 ResponseWriter 其实是定义在 net/http 包中的一个接口:

// src/net/http/
type ReponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}

我们响应客户端请求都是通过该接口的 3 个方法进行的。例如之前fmt.Fprintln(w,"Hello,Web") 其实底层调用 Write 方法。

收到请求后,多路复用器会自动创建一个 http.response 对象,它实现了 http.ResponseWriter接口,然后将该对象和请求对象作为参数传给处理器。那为什么请求对象使用的是结构指针

*http.Request ,而响应要使用接口呢

实际上,请求对象使用指针是为了能在处理逻辑中方便地获取请求信息。而响应使用接口来操作,底层也是对象指针,可以保存修改。

2接口 ResponseWriter 有 3 个方法

ResponseWriter 有 3 个方法:

  • Write
  • WriteHeader
  • Header

3.Write 方法

由于接口 ResponseWriter 拥有方法 Write([]byte) (int, error) ,所以实现了 ResponseWriter接口的结构也实现了 io.Writer 接口

	// src/io/io.go
	type Writer interface {
		Write(p []byte) (n int, err error)
	}

这也是为什么 http.ResponseWriter 类型的变量 w 能在下面代码中使用的原因( fmt.Fprintln的第一个参数接收一个 io.Writer 接口):

	fmt.Fprintln(w, "Hello World")

我们也可以直接调用 Write 方法来向响应中写入数据:

 func writeHandler(w http.ResponseWriter, r *http.Request) {
 str := `<html>
 <head><title>Go Web</title></head>
 <body><h1>直接使用 Write 方法<h1></body>
 </html>`
 w.Write([]byte(str))
 }
 mux.HandleFunc("/write", writeHandler)

如何设置状态码和响应内容的类型呢?这就是 WriteHeader 和 Header() 两个方法的作用。

4.WriteHeader 方法

WriteHeader 方法的名字带有一点误导性,它并不能用于设置响应首部。 WriteHeader 接收一个整数,并将这个整数作为 HTTP 响应的状态码返回。调用这个返回之后,可以继续对 ResponseWriter 进行写入,但是不能对响应的首部进行任何修改操作。如果用户在调用 Write 方法之前没有执行过WriteHeader 方法,那么程序默认会使用 200 作为响应的状态码。

如果,我们定义一个 API,还未定义其实现。那么请求这个 API 时,可以返回一个 501 Not Implemented 作为状态码。

   func writeHeaderHandler(w http.ResponseWriter, r *http.Request) {
   w.WriteHeader(501)
   fmt.Fprintln(w, "This API not implemented!!!")
   }
   mux.HandleFunc("/writeheader", writeHeaderHandler)

使用 curl 来测试刚刚编写的处理器

   curl -i localhost:8080/writeheader

返回

   HTTP/1.1 501 Not Implemented
   Date: Wed, 1 Jan 2020 14:15:16 GMT
   Content-Length: 28
   Content-Type: text/plain; charset=utf-8
   This API not implemented!!!

5.Header 方法

Header 方法其实返回的是一个 http.Header 类型,该类型的底层类型为 map[string][]string :

	// src/net/http/header.go
	type Header map[string][]string

类型 Header 定义了CRUD 方法,可以通过这些方法操作首部。

	func headerHandler(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Location", "http://baidu.com")
		w.WriteHeader(302)
	}

通过第一篇文章我们知道 302 表示重定向,浏览器收到该状态码时会再发起一个请求到首部中Location 指向的地址。使用 curl 测试:

curl -i localhost:8080/header

返回

HTTP/1.1 302 Found
Location: http://baidu.com
Date: Wed, 1 Jan 2020 14:17:49 GMT
Content-Length: 0

如何在浏览器中打开 localhost:8080/header ,网页会重定向到百度首页

接下来,我们看如何设置自定义的内容类型。通过 Header.Set 方法设置响应的首部 Contet-Type即可。我们编写一个返回 JSON 数据的处理器:

type User struct {
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
Age int `json:"age"`
Hobbies []string `json:"hobbies"`
}
func jsonHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
u := &User {
FirstName: "ls",
LastName: "ls",
Age: 18,
Hobbies: []string{"reading", "learning"},
}
data, _ := json.Marshal(u)
w.Write(data)
}
mux.HandleFunc("/json", jsonHandler)

通过 curl 发送请求:

curl -i localhost:8080/json

返回:

HTTP/1.1 200 OK
Content-Type: application/json
Date: Wed, 1 Jan 2020 14:31:03 GMT
Content-Length: 78
{"first_name":"ls","last_name":"ds","age":18,"hobbies":["reading","learning"]}

可以看到响应首部中类型 Content-Type 被设置成了 application/json 。类似的格式还有xml( application/xml ) /pdf( application/pdf ) /png( image/png )等等