// 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 } // get service and method service, method := pathToReceiver(a.Namespace, r.URL.Path) // create request and response req := (*cmd.DefaultOptions().Client).NewRequest(service, method, request) rsp := &api.Response{} // create the context from headers ctx := helper.RequestToContext(r) if err := (*cmd.DefaultOptions().Client).Call(ctx, req, rsp); 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 } 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)) }
// getStatus returns a status code from the error func getStatus(err error) int { // no error if err == nil { return 200 } // try get errors.Error if e, ok := err.(*errors.Error); ok { return int(e.Code) } // try parse marshalled error if e := errors.Parse(err.Error()); e.Code > 0 { return int(e.Code) } // could not parse, 500 return 500 }
func restHandler(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 } service, method := pathToReceiver(r.URL.Path) req := (*cmd.DefaultOptions().Client).NewRequest(service, method, request) rsp := &api.Response{} if err := (*cmd.DefaultOptions().Client).Call(context.Background(), req, rsp); 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 } 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)) }
// RPC Handler passes on a JSON or form encoded RPC request to // a service. func RPC(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } defer r.Body.Close() badRequest := func(description string) { e := errors.BadRequest("go.micro.rpc", description) w.WriteHeader(400) w.Write([]byte(e.Error())) } var service, method, address string var request interface{} // response content type w.Header().Set("Content-Type", "application/json") switch r.Header.Get("Content-Type") { case "application/json": var rpcReq rpcRequest d := json.NewDecoder(r.Body) d.UseNumber() if err := d.Decode(&rpcReq); err != nil { badRequest(err.Error()) return } service = rpcReq.Service method = rpcReq.Method address = rpcReq.Address request = rpcReq.Request // JSON as string if req, ok := rpcReq.Request.(string); ok { d := json.NewDecoder(strings.NewReader(req)) d.UseNumber() if err := d.Decode(&request); err != nil { badRequest("error decoding request string: " + err.Error()) return } } default: r.ParseForm() service = r.Form.Get("service") method = r.Form.Get("method") d := json.NewDecoder(strings.NewReader(r.Form.Get("request"))) d.UseNumber() if err := d.Decode(&request); err != nil { badRequest("error decoding request string: " + err.Error()) return } } if len(service) == 0 { badRequest("invalid service") return } if len(method) == 0 { badRequest("invalid method") return } // create request/response var response map[string]interface{} var err error req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) // create context ctx := helper.RequestToContext(r) // remote call if len(address) > 0 { err = (*cmd.DefaultOptions().Client).CallRemote(ctx, address, req, &response) } else { err = (*cmd.DefaultOptions().Client).Call(ctx, req, &response) } if err != nil { ce := errors.Parse(err.Error()) switch ce.Code { case 0: // assuming it's totally screwed ce.Code = 500 ce.Id = "go.micro.rpc" ce.Status = http.StatusText(500) ce.Detail = "error during request: " + ce.Detail w.WriteHeader(500) default: w.WriteHeader(int(ce.Code)) } w.Write([]byte(ce.Error())) return } b, _ := json.Marshal(response) w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) }
func rpcHandler(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } defer r.Body.Close() var service, method string var request interface{} // response content type w.Header().Set("Content-Type", "application/json") switch r.Header.Get("Content-Type") { case "application/json": b, err := ioutil.ReadAll(r.Body) if err != nil { e := errors.BadRequest("go.micro.api", err.Error()) w.WriteHeader(400) w.Write([]byte(e.Error())) return } var body map[string]interface{} err = json.Unmarshal(b, &body) if err != nil { e := errors.BadRequest("go.micro.api", err.Error()) w.WriteHeader(400) w.Write([]byte(e.Error())) return } service = body["service"].(string) method = body["method"].(string) request = body["request"] default: r.ParseForm() service = r.Form.Get("service") method = r.Form.Get("method") json.Unmarshal([]byte(r.Form.Get("request")), &request) } var response map[string]interface{} req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) err := (*cmd.DefaultOptions().Client).Call(context.Background(), req, &response) if err != nil { log.Errorf("Error calling %s.%s: %v", service, method, err) 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 } b, _ := json.Marshal(response) w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) }
// RPC Handler passes on a JSON or form encoded RPC request to // a service. func RPC(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } defer r.Body.Close() var service, method, address string var request interface{} // response content type w.Header().Set("Content-Type", "application/json") switch r.Header.Get("Content-Type") { case "application/json": b, err := ioutil.ReadAll(r.Body) if err != nil { e := errors.BadRequest("go.micro.rpc", err.Error()) w.WriteHeader(400) w.Write([]byte(e.Error())) return } var body map[string]interface{} err = json.Unmarshal(b, &body) if err != nil { e := errors.BadRequest("go.micro.rpc", err.Error()) w.WriteHeader(400) w.Write([]byte(e.Error())) return } var ok bool service, ok = body["service"].(string) if !ok { e := errors.BadRequest("go.micro.rpc", "invalid service") w.WriteHeader(400) w.Write([]byte(e.Error())) return } method, ok = body["method"].(string) if !ok { e := errors.BadRequest("go.micro.rpc", "invalid method") w.WriteHeader(400) w.Write([]byte(e.Error())) return } address, _ = body["address"].(string) req, _ := body["request"].(string) json.Unmarshal([]byte(req), &request) default: r.ParseForm() service = r.Form.Get("service") method = r.Form.Get("method") json.Unmarshal([]byte(r.Form.Get("request")), &request) } var response map[string]interface{} var err error req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, request) // remote call if len(address) > 0 { err = (*cmd.DefaultOptions().Client).CallRemote(context.Background(), address, req, &response) } else { err = (*cmd.DefaultOptions().Client).Call(context.Background(), req, &response) } if err != nil { 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 } b, _ := json.Marshal(response) w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) }
// RPCX Handler is an alternative handler which passes through an RPC request without modification func (h *rpcxHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { if r.Method != "POST" { http.Error(w, "Method not allowed", http.StatusMethodNotAllowed) return } defer r.Body.Close() // get service/method service, method := pathToReceiver(h.Namespace, r.URL.Path) ct := r.Header.Get("Content-Type") // Strip charset from Content-Type (like `application/json; charset=UTF-8`) if idx := strings.IndexRune(ct, ';'); idx >= 0 { ct = ct[:idx] } switch ct { case "application/json": // response content type w.Header().Set("Content-Type", "application/json") // get request br, err := ioutil.ReadAll(r.Body) if err != nil { e := errors.InternalServerError("go.micro.api", err.Error()) http.Error(w, e.Error(), 500) return } // use as raw json request := json.RawMessage(br) // create request/response var response json.RawMessage req := (*cmd.DefaultOptions().Client).NewJsonRequest(service, method, &request) // create context ctx := helper.RequestToContext(r) // make the call if err := (*cmd.DefaultOptions().Client).Call(ctx, req, &response); err != nil { ce := errors.Parse(err.Error()) switch ce.Code { case 0: // assuming it's totally screwed ce.Code = 500 ce.Id = "go.micro.api" ce.Status = http.StatusText(500) ce.Detail = "error during request: " + ce.Detail w.WriteHeader(500) default: w.WriteHeader(int(ce.Code)) } w.Write([]byte(ce.Error())) return } b, _ := response.MarshalJSON() w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) case "application/proto", "application/protobuf": // get request br, err := ioutil.ReadAll(r.Body) if err != nil { e := errors.InternalServerError("go.micro.api", err.Error()) http.Error(w, e.Error(), 500) return } // use as raw proto request := proto.NewMessage(br) // create request/response response := &proto.Message{} req := (*cmd.DefaultOptions().Client).NewProtoRequest(service, method, request) // create context ctx := helper.RequestToContext(r) // make the call if err := (*cmd.DefaultOptions().Client).Call(ctx, req, response); err != nil { ce := errors.Parse(err.Error()) switch ce.Code { case 0: // assuming it's totally screwed ce.Code = 500 ce.Id = "go.micro.api" ce.Status = http.StatusText(500) ce.Detail = "error during request: " + ce.Detail w.WriteHeader(500) default: w.WriteHeader(int(ce.Code)) } // response content type w.Header().Set("Content-Type", "application/json") w.Write([]byte(ce.Error())) return } b, _ := response.Marshal() w.Header().Set("Content-Type", r.Header.Get("Content-Type")) w.Header().Set("Content-Length", strconv.Itoa(len(b))) w.Write(b) default: http.Error(w, "unknown content-type", 500) return } }