handle.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. package http
  2. import (
  3. "io/ioutil"
  4. "net/http"
  5. "strings"
  6. "google.golang.org/protobuf/types/known/emptypb"
  7. "github.com/go-kratos/kratos/v2/transport/http/binding"
  8. "github.com/go-kratos/kratos/v2/encoding"
  9. "github.com/go-kratos/kratos/v2/errors"
  10. "git.ikuban.com/server/kratos-utils/http/encoding/jsonpb"
  11. _ "github.com/go-kratos/kratos/v2/transport"
  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 {
  47. if se.Code < 10000 {
  48. se.Code = 10000 + se.Code
  49. }
  50. if se.Code < 10100 && se.Message == "" {
  51. se.Message = "系统错误"
  52. }
  53. w.WriteHeader(400)
  54. }
  55. data, _ := codec.Marshal(se)
  56. _, _ = w.Write(data)
  57. }
  58. const baseContentType = "application"
  59. var (
  60. acceptHeader = http.CanonicalHeaderKey("Accept")
  61. ContentTypeHeader = http.CanonicalHeaderKey("Content-Type")
  62. )
  63. func contentType(subtype string) string {
  64. return strings.Join([]string{baseContentType, subtype}, "/")
  65. }
  66. // codecForRequest get encoding.Codec via http.Request
  67. func codecForRequest(r *http.Request) encoding.Codec {
  68. var codec encoding.Codec
  69. for _, accept := range r.Header[acceptHeader] {
  70. codeName := contentSubtype(accept)
  71. if codeName == "json" {
  72. codec = encoding.GetCodec(jsonpb.Name)
  73. break
  74. }
  75. if codec = encoding.GetCodec(codeName); codec != nil {
  76. break
  77. }
  78. }
  79. if codec == nil {
  80. codec = encoding.GetCodec(jsonpb.Name)
  81. }
  82. return codec
  83. }
  84. func contentSubtype(contentType string) string {
  85. if contentType == baseContentType {
  86. return ""
  87. }
  88. if !strings.HasPrefix(contentType, baseContentType) {
  89. return ""
  90. }
  91. switch contentType[len(baseContentType)] {
  92. case '/', ';':
  93. if i := strings.Index(contentType, ";"); i != -1 {
  94. return contentType[len(baseContentType)+1 : i]
  95. }
  96. return contentType[len(baseContentType)+1:]
  97. default:
  98. return ""
  99. }
  100. }