handle.go 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package http
  2. import (
  3. "io/ioutil"
  4. "net/http"
  5. "strings"
  6. "github.com/go-kratos/kratos/v2/transport/http/binding"
  7. "google.golang.org/protobuf/types/known/emptypb"
  8. "github.com/go-kratos/kratos/v2/encoding"
  9. "github.com/go-kratos/kratos/v2/errors"
  10. "github.com/go-kratos/kratos/v2/encoding/json"
  11. _ "github.com/go-kratos/kratos/v2/encoding/proto"
  12. )
  13. // decodeRequest decodes the request body to object.
  14. func DecodeRequest(req *http.Request, v interface{}) error {
  15. method := strings.ToUpper(req.Method)
  16. if method == "POST" || method == "PUT" || method == "DELETE" {
  17. if _, ok := v.(*emptypb.Empty); ok {
  18. return binding.BindForm(req, v)
  19. }
  20. subtype := contentSubtype(req.Header.Get(ContentTypeHeader))
  21. if codec := encoding.GetCodec(subtype); codec != nil {
  22. data, err := ioutil.ReadAll(req.Body)
  23. if err != nil {
  24. return err
  25. }
  26. return codec.Unmarshal(data, v)
  27. }
  28. }
  29. return binding.BindForm(req, v)
  30. }
  31. func ErrHandle(w http.ResponseWriter, r *http.Request, err error) {
  32. se, ok := errors.FromError(err)
  33. if !ok {
  34. se = &errors.StatusError{
  35. Code: 10500,
  36. Message: err.Error(),
  37. }
  38. }
  39. if se.Code == -1 {
  40. return
  41. }
  42. codec := codecForRequest(r)
  43. w.Header().Set(ContentTypeHeader, contentType(codec.Name()))
  44. if se.Code == 0 {
  45. w.WriteHeader(200)
  46. } else if se.Code >= 302 && se.Code <= 303 {
  47. w.WriteHeader(int(se.Code))
  48. http.Redirect(w, r, se.Message, int(se.Code))
  49. return
  50. } else {
  51. if se.Code < 10000 {
  52. se.Code = 10000 + se.Code
  53. }
  54. if se.Code < 10100 && se.Message == "" {
  55. se.Message = "系统错误"
  56. }
  57. w.WriteHeader(400)
  58. }
  59. data, _ := codec.Marshal(se)
  60. _, _ = w.Write(data)
  61. }
  62. const baseContentType = "application"
  63. var (
  64. acceptHeader = http.CanonicalHeaderKey("Accept")
  65. ContentTypeHeader = http.CanonicalHeaderKey("Content-Type")
  66. )
  67. func contentType(subtype string) string {
  68. return strings.Join([]string{baseContentType, subtype}, "/")
  69. }
  70. // codecForRequest get encoding.Codec via http.Request
  71. func codecForRequest(r *http.Request) encoding.Codec {
  72. var codec encoding.Codec
  73. for _, accept := range r.Header[acceptHeader] {
  74. codeName := contentSubtype(accept)
  75. if codeName == "json" {
  76. codec = encoding.GetCodec(json.Name)
  77. break
  78. }
  79. if codec = encoding.GetCodec(codeName); codec != nil {
  80. break
  81. }
  82. }
  83. if codec == nil {
  84. codec = encoding.GetCodec(json.Name)
  85. }
  86. return codec
  87. }
  88. func contentSubtype(contentType string) string {
  89. if contentType == baseContentType {
  90. return ""
  91. }
  92. if !strings.HasPrefix(contentType, baseContentType) {
  93. return ""
  94. }
  95. switch contentType[len(baseContentType)] {
  96. case '/', ';':
  97. if i := strings.Index(contentType, ";"); i != -1 {
  98. return contentType[len(baseContentType)+1 : i]
  99. }
  100. return contentType[len(baseContentType)+1:]
  101. default:
  102. return ""
  103. }
  104. }