|
package common
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/http"
|
|
|
|
"gopkg.in/go-playground/validator.v9"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type ErrorResponse struct {
|
|
Success bool `json:"success,omitempty"`
|
|
|
|
// Err is the error message if Success is false
|
|
Err string `json:"error,omitempty"`
|
|
|
|
// Code is set if Success is false
|
|
Code int `json:"code,omitempty"`
|
|
}
|
|
|
|
// ErrorWithCode makes an ErrorResponse with the
|
|
// provided err's Error() content, and status code.
|
|
// It panics if err is nil.
|
|
func ErrorWithCode(err error, code int) *ErrorResponse {
|
|
return &ErrorResponse{
|
|
Err: err.Error(),
|
|
Code: code,
|
|
}
|
|
}
|
|
|
|
// Ensure that ErrorResponse implements error
|
|
var _ error = (*ErrorResponse)(nil)
|
|
|
|
func (er *ErrorResponse) Error() string {
|
|
return er.Err
|
|
}
|
|
|
|
// Ensure that ErrorResponse implements httpCoder
|
|
var _ httpCoder = (*ErrorResponse)(nil)
|
|
|
|
func (er *ErrorResponse) HTTPCode() int {
|
|
return er.Code
|
|
}
|
|
|
|
var errNilBody = errors.Errorf("expecting a non-nil body")
|
|
|
|
// FparseJSON unmarshals into save, the body of the provided reader.
|
|
// Since it uses json.Unmarshal, save must be of a pointer type
|
|
// or compatible with json.Unmarshal.
|
|
func FparseJSON(r io.Reader, save interface{}) error {
|
|
if r == nil {
|
|
return errors.Wrap(errNilBody, "Reader")
|
|
}
|
|
|
|
dec := json.NewDecoder(r)
|
|
if err := dec.Decode(save); err != nil {
|
|
return errors.Wrap(err, "Decode/Unmarshal")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ParseRequestJSON unmarshals into save, the body of the
|
|
// request. It closes the body of the request after parsing.
|
|
// Since it uses json.Unmarshal, save must be of a pointer type
|
|
// or compatible with json.Unmarshal.
|
|
func ParseRequestJSON(r *http.Request, save interface{}) error {
|
|
if r == nil || r.Body == nil {
|
|
return errNilBody
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
return FparseJSON(r.Body, save)
|
|
}
|
|
|
|
// ParseRequestAndValidateJSON unmarshals into save, the body of the
|
|
// request and invokes a validator on the saved content. To ensure
|
|
// validation, make sure to set tags "validate" on your struct as
|
|
// per https://godoc.org/gopkg.in/go-playground/validator.v9.
|
|
// It closes the body of the request after parsing.
|
|
// Since it uses json.Unmarshal, save must be of a pointer type
|
|
// or compatible with json.Unmarshal.
|
|
func ParseRequestAndValidateJSON(r *http.Request, save interface{}) error {
|
|
if r == nil || r.Body == nil {
|
|
return errNilBody
|
|
}
|
|
defer r.Body.Close()
|
|
|
|
return FparseAndValidateJSON(r.Body, save)
|
|
}
|
|
|
|
// FparseAndValidateJSON like FparseJSON unmarshals into save,
|
|
// the body of the provided reader. However, it invokes the validator
|
|
// to check the set validators on your struct fields as per
|
|
// per https://godoc.org/gopkg.in/go-playground/validator.v9.
|
|
// Since it uses json.Unmarshal, save must be of a pointer type
|
|
// or compatible with json.Unmarshal.
|
|
func FparseAndValidateJSON(r io.Reader, save interface{}) error {
|
|
if err := FparseJSON(r, save); err != nil {
|
|
return err
|
|
}
|
|
return validate(save)
|
|
}
|
|
|
|
var theValidator = validator.New()
|
|
|
|
func validate(obj interface{}) error {
|
|
return errors.Wrap(theValidator.Struct(obj), "Validate")
|
|
}
|
|
|
|
// WriteSuccess JSON marshals the content provided, to an HTTP
|
|
// response, setting the provided status code and setting header
|
|
// "Content-Type" to "application/json".
|
|
func WriteSuccess(w http.ResponseWriter, data interface{}) {
|
|
WriteCode(w, data, 200)
|
|
}
|
|
|
|
// WriteCode JSON marshals content, to an HTTP response,
|
|
// setting the provided status code, and setting header
|
|
// "Content-Type" to "application/json". If JSON marshalling fails
|
|
// with an error, WriteCode instead writes out the error invoking
|
|
// WriteError.
|
|
func WriteCode(w http.ResponseWriter, out interface{}, code int) {
|
|
blob, err := json.MarshalIndent(out, "", " ")
|
|
if err != nil {
|
|
WriteError(w, err)
|
|
} else {
|
|
w.Header().Set("Content-Type", "application/json")
|
|
w.WriteHeader(code)
|
|
w.Write(blob)
|
|
}
|
|
}
|
|
|
|
type httpCoder interface {
|
|
HTTPCode() int
|
|
}
|
|
|
|
// WriteError is a convenience function to write out an
|
|
// error to an http.ResponseWriter, to send out an error
|
|
// that's structured as JSON i.e the form
|
|
// {"error": sss, "code": ddd}
|
|
// If err implements the interface HTTPCode() int,
|
|
// it will use that status code otherwise, it will
|
|
// set code to be http.StatusBadRequest
|
|
func WriteError(w http.ResponseWriter, err error) {
|
|
code := http.StatusBadRequest
|
|
if httpC, ok := err.(httpCoder); ok {
|
|
code = httpC.HTTPCode()
|
|
}
|
|
|
|
WriteCode(w, ErrorWithCode(err, code), code)
|
|
}
|