New errors API!!

This commit is contained in:
Manu Mtz-Almeida
2015-05-22 03:25:21 +02:00
parent 5f76ba2022
commit e94247f9ad
4 changed files with 107 additions and 100 deletions
+63 -70
View File
@@ -126,6 +126,11 @@ func (c *Context) AbortWithStatus(code int) {
c.Abort()
}
func (c *Context) AbortWithError(code int, err error) *errorMsg {
c.AbortWithStatus(code)
return c.Error(err)
}
func (c *Context) IsAborted() bool {
return c.index == AbortIndex
}
@@ -134,30 +139,16 @@ func (c *Context) IsAborted() bool {
/********* ERROR MANAGEMENT *********/
/************************************/
// Fail is the same as Abort plus an error message.
// Calling `context.Fail(500, err)` is equivalent to:
// ```
// context.Error("Operation aborted", err)
// context.AbortWithStatus(500)
// ```
func (c *Context) Fail(code int, err error) {
c.Error(err, "Operation aborted")
c.AbortWithStatus(code)
}
func (c *Context) ErrorTyped(err error, typ int, meta interface{}) {
c.Errors = append(c.Errors, errorMsg{
Error: err,
Flags: typ,
Meta: meta,
})
}
// Attaches an error to the current context. The error is pushed to a list of errors.
// It's a good idea to call Error for each error that occurred during the resolution of a request.
// A middleware can be used to collect all the errors and push them to a database together, print a log, or append it in the HTTP response.
func (c *Context) Error(err error, meta interface{}) {
c.ErrorTyped(err, ErrorTypeExternal, meta)
func (c *Context) Error(err error) *errorMsg {
newError := &errorMsg{
Error: err,
Flags: ErrorTypePrivate,
}
c.Errors = append(c.Errors, newError)
return newError
}
func (c *Context) LastError() error {
@@ -168,6 +159,35 @@ func (c *Context) LastError() error {
return nil
}
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
// Sets a new pair key/value just for the specified context.
// It also lazy initializes the hashmap.
func (c *Context) Set(key string, value interface{}) {
if c.Keys == nil {
c.Keys = make(map[string]interface{})
}
c.Keys[key] = value
}
// Get returns the value for the given key or an error if the key does not exist.
func (c *Context) Get(key string) (value interface{}, exists bool) {
if c.Keys != nil {
value, exists = c.Keys[key]
}
return
}
// MustGet returns the value for the given key or panics if the value doesn't exist.
func (c *Context) MustGet(key string) interface{} {
if value, exists := c.Get(key); exists {
return value
}
panic("Key \"" + key + "\" does not exist")
}
/************************************/
/************ INPUT DATA ************/
/************************************/
@@ -233,40 +253,29 @@ func (c *Context) postFormValue(key string) (string, bool) {
return "", false
}
/************************************/
/******** METADATA MANAGEMENT********/
/************************************/
// Sets a new pair key/value just for the specified context.
// It also lazy initializes the hashmap.
func (c *Context) Set(key string, value interface{}) {
if c.Keys == nil {
c.Keys = make(map[string]interface{})
}
c.Keys[key] = value
// This function checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding
// "application/xml" --> XML binding
// else --> returns an error
// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
func (c *Context) Bind(obj interface{}) error {
b := binding.Default(c.Request.Method, c.ContentType())
return c.BindWith(obj, b)
}
// Get returns the value for the given key or an error if the key does not exist.
func (c *Context) Get(key string) (value interface{}, exists bool) {
if c.Keys != nil {
value, exists = c.Keys[key]
}
return
func (c *Context) BindJSON(obj interface{}) error {
return c.BindWith(obj, binding.JSON)
}
// MustGet returns the value for the given key or panics if the value doesn't exist.
func (c *Context) MustGet(key string) interface{} {
if value, exists := c.Get(key); exists {
return value
} else {
panic("Key \"" + key + "\" does not exist")
func (c *Context) BindWith(obj interface{}, b binding.Binding) error {
if err := b.Bind(c.Request, obj); err != nil {
c.AbortWithError(400, err).Type(ErrorTypeBind)
return err
}
return nil
}
/************************************/
/********* PARSING REQUEST **********/
/************************************/
func (c *Context) ClientIP() string {
clientIP := c.Request.Header.Get("X-Real-IP")
if len(clientIP) > 0 {
@@ -284,25 +293,6 @@ func (c *Context) ContentType() string {
return filterFlags(c.Request.Header.Get("Content-Type"))
}
// This function checks the Content-Type to select a binding engine automatically,
// Depending the "Content-Type" header different bindings are used:
// "application/json" --> JSON binding
// "application/xml" --> XML binding
// else --> returns an error
// if Parses the request's body as JSON if Content-Type == "application/json" using JSON or XML as a JSON input. It decodes the json payload into the struct specified as a pointer.Like ParseBody() but this method also writes a 400 error if the json is not valid.
func (c *Context) Bind(obj interface{}) bool {
b := binding.Default(c.Request.Method, c.ContentType())
return c.BindWith(obj, b)
}
func (c *Context) BindWith(obj interface{}, b binding.Binding) bool {
if err := b.Bind(c.Request, obj); err != nil {
c.Fail(400, err)
return false
}
return true
}
/************************************/
/******** RESPONSE RENDERING ********/
/************************************/
@@ -319,8 +309,7 @@ func (c *Context) Render(code int, r render.Render) {
c.Writer.WriteHeader(code)
if err := r.Write(c.Writer); err != nil {
debugPrintError(err)
c.ErrorTyped(err, ErrorTypeInternal, nil)
c.AbortWithStatus(500)
c.AbortWithError(500, err).Type(ErrorTypeRender)
}
}
@@ -430,7 +419,7 @@ func (c *Context) Negotiate(code int, config Negotiate) {
c.XML(code, data)
default:
c.Fail(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
c.AbortWithError(http.StatusNotAcceptable, errors.New("the accepted formats are not offered by the server"))
}
}
@@ -458,6 +447,10 @@ func (c *Context) SetAccepted(formats ...string) {
c.Accepted = formats
}
/************************************/
/******** CONTENT NEGOTIATION *******/
/************************************/
func (c *Context) Deadline() (deadline time.Time, ok bool) {
return
}