iris/httperror.go
2016-06-02 05:05:54 +03:00

229 lines
8.3 KiB
Go

package iris
//taken from net/http
const (
StatusContinue = 100
StatusSwitchingProtocols = 101
StatusOK = 200
StatusCreated = 201
StatusAccepted = 202
StatusNonAuthoritativeInfo = 203
StatusNoContent = 204
StatusResetContent = 205
StatusPartialContent = 206
StatusMultipleChoices = 300
StatusMovedPermanently = 301
StatusFound = 302
StatusSeeOther = 303
StatusNotModified = 304
StatusUseProxy = 305
StatusTemporaryRedirect = 307
StatusBadRequest = 400
StatusUnauthorized = 401
StatusPaymentRequired = 402
StatusForbidden = 403
StatusNotFound = 404
StatusMethodNotAllowed = 405
StatusNotAcceptable = 406
StatusProxyAuthRequired = 407
StatusRequestTimeout = 408
StatusConflict = 409
StatusGone = 410
StatusLengthRequired = 411
StatusPreconditionFailed = 412
StatusRequestEntityTooLarge = 413
StatusRequestURITooLong = 414
StatusUnsupportedMediaType = 415
StatusRequestedRangeNotSatisfiable = 416
StatusExpectationFailed = 417
StatusTeapot = 418
StatusPreconditionRequired = 428
StatusTooManyRequests = 429
StatusRequestHeaderFieldsTooLarge = 431
StatusUnavailableForLegalReasons = 451
StatusInternalServerError = 500
StatusNotImplemented = 501
StatusBadGateway = 502
StatusServiceUnavailable = 503
StatusGatewayTimeout = 504
StatusHTTPVersionNotSupported = 505
StatusNetworkAuthenticationRequired = 511
)
var statusText = map[int]string{
StatusContinue: "Continue",
StatusSwitchingProtocols: "Switching Protocols",
StatusOK: "OK",
StatusCreated: "Created",
StatusAccepted: "Accepted",
StatusNonAuthoritativeInfo: "Non-Authoritative Information",
StatusNoContent: "No Content",
StatusResetContent: "Reset Content",
StatusPartialContent: "Partial Content",
StatusMultipleChoices: "Multiple Choices",
StatusMovedPermanently: "Moved Permanently",
StatusFound: "Found",
StatusSeeOther: "See Other",
StatusNotModified: "Not Modified",
StatusUseProxy: "Use Proxy",
StatusTemporaryRedirect: "Temporary Redirect",
StatusBadRequest: "Bad Request",
StatusUnauthorized: "Unauthorized",
StatusPaymentRequired: "Payment Required",
StatusForbidden: "Forbidden",
StatusNotFound: "Not Found",
StatusMethodNotAllowed: "Method Not Allowed",
StatusNotAcceptable: "Not Acceptable",
StatusProxyAuthRequired: "Proxy Authentication Required",
StatusRequestTimeout: "Request Timeout",
StatusConflict: "Conflict",
StatusGone: "Gone",
StatusLengthRequired: "Length Required",
StatusPreconditionFailed: "Precondition Failed",
StatusRequestEntityTooLarge: "Request Entity Too Large",
StatusRequestURITooLong: "Request URI Too Long",
StatusUnsupportedMediaType: "Unsupported Media Type",
StatusRequestedRangeNotSatisfiable: "Requested Range Not Satisfiable",
StatusExpectationFailed: "Expectation Failed",
StatusTeapot: "I'm a teapot",
StatusPreconditionRequired: "Precondition Required",
StatusTooManyRequests: "Too Many Requests",
StatusRequestHeaderFieldsTooLarge: "Request Header Fields Too Large",
StatusUnavailableForLegalReasons: "Unavailable For Legal Reasons",
StatusInternalServerError: "Internal Server Error",
StatusNotImplemented: "Not Implemented",
StatusBadGateway: "Bad Gateway",
StatusServiceUnavailable: "Service Unavailable",
StatusGatewayTimeout: "Gateway Timeout",
StatusHTTPVersionNotSupported: "HTTP Version Not Supported",
StatusNetworkAuthenticationRequired: "Network Authentication Required",
}
// StatusText returns a text for the HTTP status code. It returns the empty
// string if the code is unknown.
func StatusText(code int) string {
return statusText[code]
}
//
type (
// HTTPErrorHandler is just an object which stores a http status code and a handler
HTTPErrorHandler struct {
code int
handler HandlerFunc
}
// HTTPErrorContainer is the struct which contains the handlers which will execute if http error occurs
// One struct per Server instance, the meaning of this is that the developer can change the default error message and replace them with his/her own completely custom handlers
//
// Example of usage:
// iris.OnError(405, func (ctx *iris.Context){ c.SendStatus(405,"Method not allowed!!!")})
// and inside the handler which you have access to the current Context:
// ctx.EmitError(405)
HTTPErrorContainer struct {
// Errors contains all the httperrorhandlers
Errors []*HTTPErrorHandler
}
)
// HTTPErrorHandlerFunc creates a handler which is responsible to send a particular error to the client
func HTTPErrorHandlerFunc(statusCode int, message string) HandlerFunc {
return func(ctx *Context) {
ctx.SetStatusCode(statusCode)
ctx.SetBodyString(message)
}
}
// GetCode returns the http status code value
func (e *HTTPErrorHandler) GetCode() int {
return e.code
}
// GetHandler returns the handler which is type of HandlerFunc
func (e *HTTPErrorHandler) GetHandler() HandlerFunc {
return e.handler
}
// SetHandler sets the handler (type of HandlerFunc) to this particular ErrorHandler
func (e *HTTPErrorHandler) SetHandler(h HandlerFunc) {
e.handler = h
}
// defaultHTTPErrors creates and returns an instance of HTTPErrorContainer with default handlers
func defaultHTTPErrors() *HTTPErrorContainer {
httperrors := new(HTTPErrorContainer)
httperrors.Errors = make([]*HTTPErrorHandler, 0)
httperrors.OnError(StatusNotFound, HTTPErrorHandlerFunc(StatusNotFound, statusText[StatusNotFound]))
httperrors.OnError(StatusInternalServerError, HTTPErrorHandlerFunc(StatusInternalServerError, statusText[StatusInternalServerError]))
return httperrors
}
// GetByCode returns the error handler by it's http status code
func (he *HTTPErrorContainer) GetByCode(httpStatus int) *HTTPErrorHandler {
if he != nil {
for _, h := range he.Errors {
if h.GetCode() == httpStatus {
return h
}
}
}
return nil
}
// OnError Registers a handler for a specific http error status
func (he *HTTPErrorContainer) OnError(httpStatus int, handler HandlerFunc) {
if httpStatus == StatusOK {
return
}
if errH := he.GetByCode(httpStatus); errH != nil {
errH.SetHandler(handler)
} else {
he.Errors = append(he.Errors, &HTTPErrorHandler{code: httpStatus, handler: handler})
}
}
///TODO: the errors must have .Next too, as middlewares inside the Context, if I let it as it is then we have problem
// we cannot set a logger and a custom handler at one error because now the error handler takes only one handelrFunc and executes there from here...
// EmitError executes the handler of the given error http status code
func (he *HTTPErrorContainer) EmitError(errCode int, ctx *Context) {
ctx.ResetBody()
if errHandler := he.GetByCode(errCode); errHandler != nil {
ctx.SetStatusCode(errCode) // for any case, user can change it after if want to
errHandler.GetHandler().Serve(ctx)
} else {
//if no error is registed, then register it with the default http error text, and re-run the Emit
he.OnError(errCode, func(c *Context) {
c.SetStatusCode(errCode)
c.SetBodyString(StatusText(errCode))
})
he.EmitError(errCode, ctx)
}
}
// OnNotFound sets the handler for http status 404,
// default is a response with text: 'Not Found' and status: 404
func (he *HTTPErrorContainer) OnNotFound(handlerFunc HandlerFunc) {
he.OnError(StatusNotFound, handlerFunc)
}
// OnPanic sets the handler for http status 500,
// default is a response with text: The server encountered an unexpected condition which prevented it from fulfilling the request. and status: 500
func (he *HTTPErrorContainer) OnPanic(handlerFunc HandlerFunc) {
he.OnError(StatusInternalServerError, handlerFunc)
}