118 lines
2.8 KiB
Go
118 lines
2.8 KiB
Go
// Package api provides an http-rpc handler which provides the entire http request over rpc
|
|
package api
|
|
|
|
import (
|
|
"net/http"
|
|
|
|
goapi "github.com/micro/go-micro/api"
|
|
"github.com/micro/go-micro/api/handler"
|
|
"github.com/micro/go-micro/client"
|
|
"github.com/micro/go-micro/errors"
|
|
"github.com/micro/go-micro/selector"
|
|
"github.com/micro/go-micro/util/ctx"
|
|
api "github.com/micro/go-micro/api/proto"
|
|
)
|
|
|
|
type apiHandler struct {
|
|
opts handler.Options
|
|
s *goapi.Service
|
|
}
|
|
|
|
const (
|
|
Handler = "api"
|
|
)
|
|
|
|
// API handler is the default handler which takes api.Request and returns api.Response
|
|
func (a *apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|
request, err := requestToProto(r)
|
|
if err != nil {
|
|
er := errors.InternalServerError("go.micro.api", err.Error())
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(500)
|
|
w.Write([]byte(er.Error()))
|
|
return
|
|
}
|
|
|
|
var service *goapi.Service
|
|
|
|
if a.s != nil {
|
|
// we were given the service
|
|
service = a.s
|
|
} else if a.opts.Router != nil {
|
|
// try get service from router
|
|
s, err := a.opts.Router.Route(r)
|
|
if err != nil {
|
|
er := errors.InternalServerError("go.micro.api", err.Error())
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(500)
|
|
w.Write([]byte(er.Error()))
|
|
return
|
|
}
|
|
service = s
|
|
} else {
|
|
// we have no way of routing the request
|
|
er := errors.InternalServerError("go.micro.api", "no route found")
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(500)
|
|
w.Write([]byte(er.Error()))
|
|
return
|
|
}
|
|
|
|
// create request and response
|
|
c := a.opts.Service.Client()
|
|
req := c.NewRequest(service.Name, service.Endpoint.Name, request)
|
|
rsp := &api.Response{}
|
|
|
|
// create the context from headers
|
|
cx := ctx.FromRequest(r)
|
|
// create strategy
|
|
so := selector.WithStrategy(strategy(service.Services))
|
|
|
|
if err := c.Call(cx, req, rsp, client.WithSelectOption(so)); err != nil {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
ce := errors.Parse(err.Error())
|
|
switch ce.Code {
|
|
case 0:
|
|
w.WriteHeader(500)
|
|
default:
|
|
w.WriteHeader(int(ce.Code))
|
|
}
|
|
w.Write([]byte(ce.Error()))
|
|
return
|
|
} else if rsp.StatusCode == 0 {
|
|
rsp.StatusCode = http.StatusOK
|
|
}
|
|
|
|
for _, header := range rsp.GetHeader() {
|
|
for _, val := range header.Values {
|
|
w.Header().Add(header.Key, val)
|
|
}
|
|
}
|
|
|
|
if len(w.Header().Get("Content-Type")) == 0 {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
}
|
|
|
|
w.WriteHeader(int(rsp.StatusCode))
|
|
w.Write([]byte(rsp.Body))
|
|
}
|
|
|
|
func (a *apiHandler) String() string {
|
|
return "api"
|
|
}
|
|
|
|
func NewHandler(opts ...handler.Option) handler.Handler {
|
|
options := handler.NewOptions(opts...)
|
|
return &apiHandler{
|
|
opts: options,
|
|
}
|
|
}
|
|
|
|
func WithService(s *goapi.Service, opts ...handler.Option) handler.Handler {
|
|
options := handler.NewOptions(opts...)
|
|
return &apiHandler{
|
|
opts: options,
|
|
s: s,
|
|
}
|
|
}
|