api_response.go 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. package http_api
  2. import (
  3. "encoding/json"
  4. "fmt"
  5. "io"
  6. "net/http"
  7. "time"
  8. "github.com/julienschmidt/httprouter"
  9. "github.com/nsqio/nsq/internal/lg"
  10. )
  11. type Decorator func(APIHandler) APIHandler
  12. type APIHandler func(http.ResponseWriter, *http.Request, httprouter.Params) (interface{}, error)
  13. type Err struct {
  14. Code int
  15. Text string
  16. }
  17. func (e Err) Error() string {
  18. return e.Text
  19. }
  20. func acceptVersion(req *http.Request) int {
  21. if req.Header.Get("accept") == "application/vnd.nsq; version=1.0" {
  22. return 1
  23. }
  24. return 0
  25. }
  26. func PlainText(f APIHandler) APIHandler {
  27. return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  28. code := 200
  29. data, err := f(w, req, ps)
  30. if err != nil {
  31. code = err.(Err).Code
  32. data = err.Error()
  33. }
  34. switch d := data.(type) {
  35. case string:
  36. w.WriteHeader(code)
  37. io.WriteString(w, d)
  38. case []byte:
  39. w.WriteHeader(code)
  40. w.Write(d)
  41. default:
  42. panic(fmt.Sprintf("unknown response type %T", data))
  43. }
  44. return nil, nil
  45. }
  46. }
  47. func V1(f APIHandler) APIHandler {
  48. return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  49. data, err := f(w, req, ps)
  50. if err != nil {
  51. RespondV1(w, err.(Err).Code, err)
  52. return nil, nil
  53. }
  54. RespondV1(w, 200, data)
  55. return nil, nil
  56. }
  57. }
  58. func RespondV1(w http.ResponseWriter, code int, data interface{}) {
  59. var response []byte
  60. var err error
  61. var isJSON bool
  62. if code == 200 {
  63. switch data.(type) {
  64. case string:
  65. response = []byte(data.(string))
  66. case []byte:
  67. response = data.([]byte)
  68. case nil:
  69. response = []byte{}
  70. default:
  71. isJSON = true
  72. response, err = json.Marshal(data)
  73. if err != nil {
  74. code = 500
  75. data = err
  76. }
  77. }
  78. }
  79. if code != 200 {
  80. isJSON = true
  81. response, _ = json.Marshal(struct {
  82. Message string `json:"message"`
  83. }{fmt.Sprintf("%s", data)})
  84. }
  85. if isJSON {
  86. w.Header().Set("Content-Type", "application/json; charset=utf-8")
  87. }
  88. w.Header().Set("X-SMQ-Content-Type", "smq; version=1.0")
  89. w.WriteHeader(code)
  90. w.Write(response)
  91. }
  92. func Decorate(f APIHandler, ds ...Decorator) httprouter.Handle {
  93. decorated := f
  94. for _, decorate := range ds {
  95. decorated = decorate(decorated)
  96. }
  97. return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
  98. decorated(w, req, ps)
  99. }
  100. }
  101. func Log(logf lg.AppLogFunc) Decorator {
  102. return func(f APIHandler) APIHandler {
  103. return func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  104. start := time.Now()
  105. response, err := f(w, req, ps)
  106. elapsed := time.Since(start)
  107. status := 200
  108. if e, ok := err.(Err); ok {
  109. status = e.Code
  110. }
  111. logf(lg.INFO, "%d %s %s (%s) %s",
  112. status, req.Method, req.URL.RequestURI(), req.RemoteAddr, elapsed)
  113. return response, err
  114. }
  115. }
  116. }
  117. func LogPanicHandler(logf lg.AppLogFunc) func(w http.ResponseWriter, req *http.Request, p interface{}) {
  118. return func(w http.ResponseWriter, req *http.Request, p interface{}) {
  119. logf(lg.ERROR, "panic in HTTP handler - %s", p)
  120. Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  121. return nil, Err{500, "INTERNAL_ERROR"}
  122. }, Log(logf), V1)(w, req, nil)
  123. }
  124. }
  125. func LogNotFoundHandler(logf lg.AppLogFunc) http.Handler {
  126. return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  127. Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  128. return nil, Err{404, "NOT_FOUND"}
  129. }, Log(logf), V1)(w, req, nil)
  130. })
  131. }
  132. func LogMethodNotAllowedHandler(logf lg.AppLogFunc) http.Handler {
  133. return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
  134. Decorate(func(w http.ResponseWriter, req *http.Request, ps httprouter.Params) (interface{}, error) {
  135. return nil, Err{405, "METHOD_NOT_ALLOWED"}
  136. }, Log(logf), V1)(w, req, nil)
  137. })
  138. }