ProblemDetails is a Error Handler base on RFC 7807 standard to map our error to standardized error payload to client. The data model for problem details is a JSON object; when formatted as a JSON document, it uses the
application/problem+json
media type and for XML format it uses theapplication/problem+xml
media type. By defining machine-readable details of HTTP errors, we can avoid defining new error response formats for HTTP APIs.
Our problem details response body and headers will be look like this:
// Response body
{
"status": 400, // The HTTP status code generated on the problem occurrence
"title": "bad-request", // A short human-readable problem summary
"detail": "We have a bad request in our endpoint", // A human-readable explanation for what exactly happened
"type": "https://httpstatuses.io/400", // URI reference to identify the problem type
"instance": "/sample1", // URI reference of the occurrence
"stackTrace": "some more trace for error", // More trace information error for what exactly happened
}
// Response headers
content-type: application/problem+json
date: Thu,29 Sep 2022 14:07:23 GMT
There are some samples for using this package on top of Echo here and for Gin here.
go get github.com/meysamhadeli/problem-details
For handling our error we need to specify an Error Handler
on top of Echo
framework:
// EchoErrorHandler middleware for handle problem details error on echo
func EchoErrorHandler(error error, c echo.Context) {
// add custom map problem details here...
// resolve problem details error from response in echo
if !c.Response().Committed {
if _, err := problem.ResolveProblemDetails(c.Response(), c.Request(), error); err != nil {
log.Error(err)
}
}
}
In this sample we map status code StatusBadGateway
to StatusUnauthorized
base on handler config to problem details error.
// handle specific status code to problem details error
func sample1(c echo.Context) error {
err := errors.New("We have a specific status code error in our endpoint")
return echo.NewHTTPError(http.StatusBadGateway, err)
}
// problem details handler config
problem.MapStatus(http.StatusBadGateway, func() problem.ProblemDetailErr {
return &problem.ProblemDetail{
Status: http.StatusUnauthorized,
Title: "unauthorized",
Detail: error.Error(),
}
})
In this sample we map custom error type to problem details error.
// handle custom type error to problem details error
func sample2(c echo.Context) error {
err := errors.New("We have a custom type error in our endpoint")
return custom_errors.BadRequestError{InternalError: err}
}
// problem details handler config
problem.Map[custom_errors.BadRequestError](func() problem.ProblemDetailErr {
return &problem.ProblemDetail{
Status: http.StatusBadRequest,
Title: "bad request",
Detail: error.Error(),
}
})
For handling our error we need to specify an Error Handler
on top of Gin
framework:
// GinErrorHandler middleware for handle problem details error on gin
func GinErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
for _, err := range c.Errors {
// add custom map problem details here...
if _, err := problem.ResolveProblemDetails(c.Writer, c.Request, err); err != nil {
log.Error(err)
}
}
}
}
In this sample we map status code StatusBadGateway
to StatusUnauthorized
base on handler config to problem details error.
// handle specific status code to problem details error
func sample1(c *gin.Context) {
err := errors.New("We have a specific status code error in our endpoint")
_ = c.AbortWithError(http.StatusBadGateway, err)
}
// problem details handler config
problem.MapStatus(http.StatusBadGateway, func() problem.ProblemDetailErr {
return &problem.ProblemDetail{
Status: http.StatusUnauthorized,
Title: "unauthorized",
Detail: err.Error(),
}
})
In this sample we map custom error type to problem details error.
// handle custom type error to problem details error
func sample2(c *gin.Context) {
err := errors.New("We have a custom type error in our endpoint")
customBadRequestError := custom_errors.BadRequestError{InternalError: err}
_ = c.Error(customBadRequestError)
}
// problem details handler config
problem.Map[custom_errors.BadRequestError](func() problem.ProblemDetailErr {
return &problem.ProblemDetail{
Status: http.StatusBadRequest,
Title: "bad request",
Detail: err.Error(),
}
})
We support custom problem details error for create more flexibility response error:
// custom problem details
type CustomProblemDetail struct {
problem.ProblemDetailErr
Description string `json:"description,omitempty"`
AdditionalInfo string `json:"additionalInfo,omitempty"`
}
// problem details handler config
problem.Map[custom_errors.ConflictError](func() problem.ProblemDetailErr {
return &custom_problems.CustomProblemDetail{
ProblemDetailErr: &problem.ProblemDetail{
Status: http.StatusConflict,
Title: "conflict",
Detail: error.Error(),
},
AdditionalInfo: "some additional info...",
Description: "some description...",
}
})
If you like my work, feel free to:
- ⭐ this repository. And we will be happy together :)
Thanks a bunch for supporting me!
Thanks to all contributors, you're awesome and this wouldn't be possible without you! The goal is to build a categorized community-driven collection of very well-known resources.
Please follow this contribution guideline to submit a pull request or create the issue.