marshaler_registry.go 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091
  1. package runtime
  2. import (
  3. "errors"
  4. "net/http"
  5. )
  6. // MIMEWildcard is the fallback MIME type used for requests which do not match
  7. // a registered MIME type.
  8. const MIMEWildcard = "*"
  9. var (
  10. acceptHeader = http.CanonicalHeaderKey("Accept")
  11. contentTypeHeader = http.CanonicalHeaderKey("Content-Type")
  12. defaultMarshaler = &JSONPb{OrigName: true}
  13. )
  14. // MarshalerForRequest returns the inbound/outbound marshalers for this request.
  15. // It checks the registry on the ServeMux for the MIME type set by the Content-Type header.
  16. // If it isn't set (or the request Content-Type is empty), checks for "*".
  17. // If there are multiple Content-Type headers set, choose the first one that it can
  18. // exactly match in the registry.
  19. // Otherwise, it follows the above logic for "*"/InboundMarshaler/OutboundMarshaler.
  20. func MarshalerForRequest(mux *ServeMux, r *http.Request) (inbound Marshaler, outbound Marshaler) {
  21. for _, acceptVal := range r.Header[acceptHeader] {
  22. if m, ok := mux.marshalers.mimeMap[acceptVal]; ok {
  23. outbound = m
  24. break
  25. }
  26. }
  27. for _, contentTypeVal := range r.Header[contentTypeHeader] {
  28. if m, ok := mux.marshalers.mimeMap[contentTypeVal]; ok {
  29. inbound = m
  30. break
  31. }
  32. }
  33. if inbound == nil {
  34. inbound = mux.marshalers.mimeMap[MIMEWildcard]
  35. }
  36. if outbound == nil {
  37. outbound = inbound
  38. }
  39. return inbound, outbound
  40. }
  41. // marshalerRegistry is a mapping from MIME types to Marshalers.
  42. type marshalerRegistry struct {
  43. mimeMap map[string]Marshaler
  44. }
  45. // add adds a marshaler for a case-sensitive MIME type string ("*" to match any
  46. // MIME type).
  47. func (m marshalerRegistry) add(mime string, marshaler Marshaler) error {
  48. if len(mime) == 0 {
  49. return errors.New("empty MIME type")
  50. }
  51. m.mimeMap[mime] = marshaler
  52. return nil
  53. }
  54. // makeMarshalerMIMERegistry returns a new registry of marshalers.
  55. // It allows for a mapping of case-sensitive Content-Type MIME type string to runtime.Marshaler interfaces.
  56. //
  57. // For example, you could allow the client to specify the use of the runtime.JSONPb marshaler
  58. // with a "application/jsonpb" Content-Type and the use of the runtime.JSONBuiltin marshaler
  59. // with a "application/json" Content-Type.
  60. // "*" can be used to match any Content-Type.
  61. // This can be attached to a ServerMux with the marshaler option.
  62. func makeMarshalerMIMERegistry() marshalerRegistry {
  63. return marshalerRegistry{
  64. mimeMap: map[string]Marshaler{
  65. MIMEWildcard: defaultMarshaler,
  66. },
  67. }
  68. }
  69. // WithMarshalerOption returns a ServeMuxOption which associates inbound and outbound
  70. // Marshalers to a MIME type in mux.
  71. func WithMarshalerOption(mime string, marshaler Marshaler) ServeMuxOption {
  72. return func(mux *ServeMux) {
  73. if err := mux.marshalers.add(mime, marshaler); err != nil {
  74. panic(err)
  75. }
  76. }
  77. }