Skip to content

Commit

Permalink
Merge pull request tendermint#33 from orijtech/http-utils
Browse files Browse the repository at this point in the history
http: http-utils added after extraction
  • Loading branch information
ethanfrey authored Aug 2, 2017
2 parents 2f6f3e6 + d67a621 commit 7537298
Show file tree
Hide file tree
Showing 2 changed files with 403 additions and 0 deletions.
153 changes: 153 additions & 0 deletions common/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
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)
}
Loading

0 comments on commit 7537298

Please sign in to comment.