package http import ( "encoding/json" "fmt" "google.golang.org/genproto/googleapis/rpc/errdetails" "google.golang.org/grpc/status" "io" "net/http" "strings" ) type Response struct { Code int `json:"code"` Reason string `json:"reason"` Message string `json:"message"` Metadata interface{} `json:"metadata"` w http.ResponseWriter ct string } func NewResponse(w http.ResponseWriter) *Response { return &Response{ w: w, } } func (r *Response) StatusCode(code int) *Response { r.Code = code return r } func (r *Response) ContentType(ct string) *Response { r.ct = ct return r } func (r *Response) getCode(defaultCode int) int { if r.Code != 0 { return r.Code } return defaultCode } func (r *Response) getContentType(defaultContentType string) string { if r.ct != "" { return r.ct } return defaultContentType } func (r *Response) Error(err error) { r.buildResponseBody(err) r.w.Header().Set("Content-Type", r.getContentType("application/json")) r.w.WriteHeader(r.getCode(http.StatusInternalServerError)) body, err := json.Marshal(r) if err != nil { r.w.Write([]byte(err.Error())) return } r.w.Write(body) } func (r *Response) Json(body []byte) { r.w.Header().Set("Content-Type", r.getContentType("application/json")) r.w.WriteHeader(r.getCode(http.StatusOK)) r.w.Write(body) } func (r *Response) Raw(rc io.Reader) { r.w.Header().Set("Content-Type", r.getContentType("application/octet-stream")) r.w.WriteHeader(r.getCode(http.StatusOK)) io.Copy(r.w, rc) } func (r *Response) Redirect(url string) { r.w.Header().Set("Location", url) r.w.WriteHeader(r.getCode(http.StatusOK)) r.w.Write([]byte("")) } func (r *Response) JsonOk(ok bool) { j, _ := json.Marshal(map[string]bool{ "ok": ok, }) r.Json(j) } func (r *Response) Abort() { r.ContentType("text/plain").Raw(strings.NewReader(http.StatusText(r.getCode(http.StatusInternalServerError)))) } func (r *Response) Download(body io.Reader, filename string) { r.w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=%s", filename)) r.Raw(body) } func (r *Response) buildResponseBody(err error) { s := status.Convert(err) pb := s.Proto() st := httpStatusFromGrpcCode(s.Code()) if len(s.Details()) > 0 { for _, detail := range s.Details() { switch d := detail.(type) { case *errdetails.ErrorInfo: r.Reason = d.Reason r.Metadata = d.Metadata break } } } else { r.Reason = "UNKNOWN_ERROR" } r.Message = pb.GetMessage() r.Code = st }