ProblemDetails is an Error Handler
base on RFC 7807 standard to map our error to standardized error payload to client. For implement this approach, we use It's a JSON or XML format, when formatted as a JSON document, it uses the "application/problem+json"
media type and for XML format, it uses the "application/problem+xml"
media type. This document help us to defines machine-readable details of errors in an HTTP response to avoid the need to define 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
}
// 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.
Installation
go get github.com/meysamhadeli/problem-details
Web-Frameworks
Echo
Error Handler:
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)
}
}
}
Map Status Code Error:
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.New(http.StatusUnauthorized, "unauthorized", err.Error())
})
Map Custom Type 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.New(http.StatusBadRequest, "bad request", error.Error())
})
Gin
Error Handler:
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)
}
}
}
}
Map Status Code Error:
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.New(http.StatusUnauthorized, "unauthorized", err.Error())
})
Map Custom Type 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.New(http.StatusBadRequest, "bad request", error.Error())
})
Custom Problem Details:
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.New(http.StatusConflict, "conflict", error.Error()),
AdditionalInfo: "some additional info...",
Description: "some description...",
}
})
Support
If you like my work, feel free to:
- ⭐ this repository. And we will be happy together :)
Thanks a bunch for supporting me!
Contribution
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.