forked from tendermint/tmlibs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request tendermint#33 from orijtech/http-utils
http: http-utils added after extraction
- Loading branch information
Showing
2 changed files
with
403 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
Oops, something went wrong.