github.com/Augustu/go-micro/v2@v2.9.3/api/handler/api/api.go (about) 1 // Package api provides an http-rpc handler which provides the entire http request over rpc 2 package api 3 4 import ( 5 "net/http" 6 7 goapi "github.com/Augustu/go-micro/v2/api" 8 "github.com/Augustu/go-micro/v2/api/handler" 9 api "github.com/Augustu/go-micro/v2/api/proto" 10 "github.com/Augustu/go-micro/v2/client" 11 "github.com/Augustu/go-micro/v2/client/selector" 12 "github.com/Augustu/go-micro/v2/errors" 13 "github.com/Augustu/go-micro/v2/util/ctx" 14 ) 15 16 type apiHandler struct { 17 opts handler.Options 18 s *goapi.Service 19 } 20 21 const ( 22 Handler = "api" 23 ) 24 25 // API handler is the default handler which takes api.Request and returns api.Response 26 func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 27 bsize := handler.DefaultMaxRecvSize 28 if a.opts.MaxRecvSize > 0 { 29 bsize = a.opts.MaxRecvSize 30 } 31 32 r.Body = http.MaxBytesReader(w, r.Body, bsize) 33 request, err := requestToProto(r) 34 if err != nil { 35 er := errors.InternalServerError("go.micro.api", err.Error()) 36 w.Header().Set("Content-Type", "application/json") 37 w.WriteHeader(500) 38 w.Write([]byte(er.Error())) 39 return 40 } 41 42 var service *goapi.Service 43 44 if a.s != nil { 45 // we were given the service 46 service = a.s 47 } else if a.opts.Router != nil { 48 // try get service from router 49 s, err := a.opts.Router.Route(r) 50 if err != nil { 51 er := errors.InternalServerError("go.micro.api", err.Error()) 52 w.Header().Set("Content-Type", "application/json") 53 w.WriteHeader(500) 54 w.Write([]byte(er.Error())) 55 return 56 } 57 service = s 58 } else { 59 // we have no way of routing the request 60 er := errors.InternalServerError("go.micro.api", "no route found") 61 w.Header().Set("Content-Type", "application/json") 62 w.WriteHeader(500) 63 w.Write([]byte(er.Error())) 64 return 65 } 66 67 // create request and response 68 c := a.opts.Client 69 req := c.NewRequest(service.Name, service.Endpoint.Name, request) 70 rsp := &api.Response{} 71 72 // create the context from headers 73 cx := ctx.FromRequest(r) 74 // create strategy 75 so := selector.WithStrategy(strategy(service.Services)) 76 77 if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil { 78 w.Header().Set("Content-Type", "application/json") 79 ce := errors.Parse(err.Error()) 80 switch ce.Code { 81 case 0: 82 w.WriteHeader(500) 83 default: 84 w.WriteHeader(int(ce.Code)) 85 } 86 w.Write([]byte(ce.Error())) 87 return 88 } else if rsp.StatusCode == 0 { 89 rsp.StatusCode = http.StatusOK 90 } 91 92 for _, header := range rsp.GetHeader() { 93 for _, val := range header.Values { 94 w.Header().Add(header.Key, val) 95 } 96 } 97 98 if len(w.Header().Get("Content-Type")) == 0 { 99 w.Header().Set("Content-Type", "application/json") 100 } 101 102 w.WriteHeader(int(rsp.StatusCode)) 103 w.Write([]byte(rsp.Body)) 104 } 105 106 func (a *apiHandler) String() string { 107 return "api" 108 } 109 110 func NewHandler(opts ...handler.Option) handler.Handler { 111 options := handler.NewOptions(opts...) 112 return &apiHandler{ 113 opts: options, 114 } 115 } 116 117 func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler { 118 options := handler.NewOptions(opts...) 119 return &apiHandler{ 120 opts: options, 121 s: s, 122 } 123 }