Go如何响应http请求?

https://juejin.im/post/5ca0a2256fb9a05e6f7af992

在Web应用程序中,每个HTTP事务都由请求(Request)和响应(Response)构成,这次我们讲讲Go如何处理Web中的数据响应。

如果想了解Go如果处理Web请求的,可参考我的另一篇文章《Go Web如何处理Web请求?》

Web数据响应

Web的响应与请求结构是类似的,响应分为三个部分:响应行、响应头部、响应体。

  1. 响应行:协议、响应状态码和状态描述,如: HTTP/1.1 200 OK
  2. 响应头部:包含各种头部字段信息,如cookie,Content-Type等头部信息。
  3. 响应体:携带客户端想要的数据,格式与编码由头部的Content-Type决定。

响应状态码的有固定取值和意义:

  • 100~199:表示服务端成功客户端接收请求,要求客户端继续提交下一次请求才能完成整个处理过程。
  • 200~299:表示服务端成功接收请求并已完成整个处理过程。最常用就是:200
  • 300~399:为完成请求,客户端需进一步细化请求。比较常用的如:客户端请求的资源已经移动一个新地址使用302表示将资源重定向,客户端请求的资源未发生改变,使用304,告诉客户端从本地缓存中获取。
  • 400~499:客户端的请求有错误,如:404表示你请求的资源在web服务器中找不到,403表示服务器拒绝客户端的访问,一般是权限不够。
  • 500~599:服务器端出现错误,最常用的是:500

Go处理Web数据响应

Go将http响应封装在http.ResponseWriter结构体中,ResponseWriter的定义很简单,一共只有三个方法。

ResponseWriter

在net/http源码包中,http.ResponseWriter的结构信息定义如下所示:

  1. type ResponseWriter interface {
  2. Header() Header //头部
  3. Write([]byte) (int, error) //写入方法
  4. WriteHeader(statusCode int)//状态码
  5. }
1. Header方法

header方法返回http.Header结构体,用于设置响应头部信息,http.Header数据类型map,定义如下:

  1. type Header map[string][]string

http.Header的方法列表如下:

  1. type Header
  2. func (h Header) Add(key, value string)
  3. func (h Header) Del(key string)
  4. func (h Header) Get(key string) string
  5. func (h Header) Set(key, value string)
  6. func (h Header) Write(w io.Writer) error
  7. func (h Header) WriteSubset(w io.Writer, exclude map[string]bool) error
2.Writer()方法

Write方法定义如下,用于向客户端返回数据流,与io.Writer中的write方法的定义一致,是Go语言io流中标准方法。

  1. Write([]byte) (int, error)
3.WriterHeader方法

writerHeader方法的定义如下所示:

  1. WriteHeader(statusCode int)

参数statusCode表示响应码,其取值可以为http包中已经定义好的常量值:

  1. const (
  2. StatusContinue = 100 // RFC 7231, 6.2.1
  3. StatusSwitchingProtocols = 101 // RFC 7231, 6.2.2
  4. StatusProcessing = 102 // RFC 2518, 10.1
  5. StatusOK = 200 // RFC 7231, 6.3.1
  6. StatusCreated = 201 // RFC 7231, 6.3.2
  7. StatusAccepted = 202 // RFC 7231, 6.3.3
  8. StatusNonAuthoritativeInfo = 203 // RFC 7231, 6.3.4
  9. StatusNoContent = 204 // RFC 7231, 6.3.5
  10. StatusResetContent = 205 // RFC 7231, 6.3.6
  11. StatusPartialContent = 206 // RFC 7233, 4.1
  12. StatusMultiStatus = 207 // RFC 4918, 11.1
  13. StatusAlreadyReported = 208 // RFC 5842, 7.1
  14. StatusIMUsed = 226 // RFC 3229, 10.4.1
  15. StatusMultipleChoices = 300 // RFC 7231, 6.4.1
  16. StatusMovedPermanently = 301 // RFC 7231, 6.4.2
  17. StatusFound = 302 // RFC 7231, 6.4.3
  18. StatusSeeOther = 303 // RFC 7231, 6.4.4
  19. StatusNotModified = 304 // RFC 7232, 4.1
  20. StatusUseProxy = 305 // RFC 7231, 6.4.5
  21. StatusTemporaryRedirect = 307 // RFC 7231, 6.4.7
  22. StatusPermanentRedirect = 308 // RFC 7538, 3
  23. StatusBadRequest = 400 // RFC 7231, 6.5.1
  24. StatusUnauthorized = 401 // RFC 7235, 3.1
  25. StatusPaymentRequired = 402 // RFC 7231, 6.5.2
  26. StatusForbidden = 403 // RFC 7231, 6.5.3
  27. StatusNotFound = 404 // RFC 7231, 6.5.4
  28. StatusMethodNotAllowed = 405 // RFC 7231, 6.5.5
  29. StatusNotAcceptable = 406 // RFC 7231, 6.5.6
  30. StatusProxyAuthRequired = 407 // RFC 7235, 3.2
  31. StatusRequestTimeout = 408 // RFC 7231, 6.5.7
  32. StatusConflict = 409 // RFC 7231, 6.5.8
  33. StatusGone = 410 // RFC 7231, 6.5.9
  34. StatusLengthRequired = 411 // RFC 7231, 6.5.10
  35. StatusPreconditionFailed = 412 // RFC 7232, 4.2
  36. StatusRequestEntityTooLarge = 413 // RFC 7231, 6.5.11
  37. StatusRequestURITooLong = 414 // RFC 7231, 6.5.12
  38. StatusUnsupportedMediaType = 415 // RFC 7231, 6.5.13
  39. StatusRequestedRangeNotSatisfiable = 416 // RFC 7233, 4.4
  40. StatusExpectationFailed = 417 // RFC 7231, 6.5.14
  41. StatusTeapot = 418 // RFC 7168, 2.3.3
  42. StatusMisdirectedRequest = 421 // RFC 7540, 9.1.2
  43. StatusUnprocessableEntity = 422 // RFC 4918, 11.2
  44. StatusLocked = 423 // RFC 4918, 11.3
  45. StatusFailedDependency = 424 // RFC 4918, 11.4
  46. StatusTooEarly = 425 // RFC 8470, 5.2.
  47. StatusUpgradeRequired = 426 // RFC 7231, 6.5.15
  48. StatusPreconditionRequired = 428 // RFC 6585, 3
  49. StatusTooManyRequests = 429 // RFC 6585, 4
  50. StatusRequestHeaderFieldsTooLarge = 431 // RFC 6585, 5
  51. StatusUnavailableForLegalReasons = 451 // RFC 7725, 3
  52. StatusInternalServerError = 500 // RFC 7231, 6.6.1
  53. StatusNotImplemented = 501 // RFC 7231, 6.6.2
  54. StatusBadGateway = 502 // RFC 7231, 6.6.3
  55. StatusServiceUnavailable = 503 // RFC 7231, 6.6.4
  56. StatusGatewayTimeout = 504 // RFC 7231, 6.6.5
  57. StatusHTTPVersionNotSupported = 505 // RFC 7231, 6.6.6
  58. StatusVariantAlsoNegotiates = 506 // RFC 2295, 8.1
  59. StatusInsufficientStorage = 507 // RFC 4918, 11.5
  60. StatusLoopDetected = 508 // RFC 5842, 7.2
  61. StatusNotExtended = 510 // RFC 2774, 7
  62. StatusNetworkAuthenticationRequired = 511 // RFC 6585, 6
  63. )
4. 示例
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. )
  6. func main() {
  7. http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
  8. header := writer.Header()
  9. header.Add("Content-Type","application/json")
  10. writer.WriteHeader(http.StatusBadGateway)
  11. fmt.Fprintln(writer,"Web响应")
  12. })
  13. http.ListenAndServe(":8080",nil)
  14. }

在浏览器控制台的Network查看运行结果:






注意区分Cookie与Session之间的区别,Cookie用服务端保存某些信息到客户端的,用于识别用户,一种用户追踪机制,而Session则是服务端实现用户多次请求之间保持会话的机制;两者可以配合使用,也可以独立使用。

net/http包提供了SetCookie方法用于向客户端写入Cookie.

  1. func SetCookie(w ResponseWriter, cookie *Cookie)

第一个参数为ResponseWriter结构体,而第二个参数则Cookie结构体,其定义如下:

  1. type Cookie struct {
  2. Name string
  3. Value string
  4. Path string // optional
  5. Domain string // optional
  6. Expires time.Time // optional
  7. RawExpires string // for reading cookies only
  8. // MaxAge=0 means no 'Max-Age' attribute specified.
  9. // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
  10. // MaxAge>0 means Max-Age attribute present and given in seconds
  11. MaxAge int
  12. Secure bool
  13. HttpOnly bool
  14. SameSite SameSite // Go 1.11
  15. Raw string
  16. Unparsed []string // Raw text of unparsed attribute-value pairs
  17. }
示例
  1. package main
  2. import (
  3. "net/http"
  4. "time"
  5. )
  6. func main() {
  7. http.HandleFunc("/test", func(writer http.ResponseWriter, request *http.Request) {
  8. expire := time.Now()
  9. expire.AddDate(0,0,3)
  10. cookie := &http.Cookie{Name:"Auth",Value:"test",Expires:expire}
  11. http.SetCookie(writer,cookie)
  12. })
  13. http.ListenAndServe(":8080",nil)
  14. }

运行结果






JSON响应

Go标准库并没有封装直接向客户端响应JSON数据的方法,不过,自己封装一个也很简单的,可以使用encoding/json包的Encoder结构体,将JSON数据写入响应数据流。

示例
  1. package main
  2. import (
  3. "encoding/json"
  4. "log"
  5. "net/http"
  6. )
  7. func main() {
  8. http.HandleFunc("/profile", func(writer http.ResponseWriter, request *http.Request) {
  9. data := map[string]string{
  10. "username": "小明",
  11. "email": "xiaoming@163.com",
  12. }
  13. err := JSON(writer, data)
  14. check(err)
  15. })
  16. http.ListenAndServe(":8080", nil)
  17. }
  18. func check(err error) {
  19. if err != nil {
  20. log.Fatal(err)
  21. }
  22. }
  23. func JSON(w http.ResponseWriter, data interface{}) error {
  24. w.Header().Set("Content-Type", "application/json")
  25. encoder := json.NewEncoder(w)
  26. return encoder.Encode(data)
  27. }

HTML模板

虽然在前后端分离的大趋势下,后端开发更多时候是以接口api响应JSON的方式返回数据给前端,但仍然有些简单的业务,是由后端直接将HTML模板返回由浏览器,由浏览器渲染。

Go语言可以使用html/template包渲染HTML模板。

示例
  1. package main
  2. import (
  3. "html/template"
  4. "log"
  5. "net/http"
  6. )
  7. const tpl = `
  8. <!DOCTYPE html>
  9. <html>
  10. <head>
  11. <meta charset="UTF-8">
  12. <title>{{.Title}}</title>
  13. </head>
  14. <body>
  15. {{range .Items}}<div>{{ . }}</div>{{else}}<div><strong>no rows</strong></div>{{end}}
  16. </body>
  17. </html>`
  18. func main() {
  19. t, err := template.New("webpage").Parse(tpl)
  20. check(err)
  21. data := struct {
  22. Title string
  23. Items []string
  24. }{
  25. Title: "我的第一个HTML页面",
  26. Items: []string{
  27. "我的相册",
  28. "我的博客",
  29. },
  30. }
  31. http.HandleFunc("/profile", func(writer http.ResponseWriter, request *http.Request) {
  32. err = t.Execute(writer, data)
  33. check(err)
  34. })
  35. http.ListenAndServe(":8080", nil)
  36. }
  37. func check(err error) {
  38. if err != nil {
  39. log.Fatal(err)
  40. }
  41. }

小结

Go语言在net/http包中对Web开发提供了很好的支持,让开发者在使用Go进行Web应用开发时,几乎不需要使用任何的Web框架,便可完成业务逻辑开发的工作。

ft_authoradmin  ft_create_time2019-08-03 17:12
 ft_update_time2019-08-03 17:12