error-handling-spring-boot-starter
error-handling-spring-boot-starter copied to clipboard
Support ProblemDetail
Spring is supporting new way to expose errors using ProblemDetail. Please support in the starter
I had a look and I could write a custom serializer that uses ProblemDetail
, but I am having trouble knowing how to map. By default, this library uses code
and message
like this:
{
"code": "USER_NOT_FOUND",
"message": "Could not find user with id 123"
}
The code
seems to be lacking in the specification. The closest seems to be type
, but that expects an URI which is not something we have available.
How do you envision this working?
One idea might be to have the user configure a base URI for all error types. For example:
error.handling.rfc7807.base-uri=https://api.myservice.com/errors
Then the example could become:
{
"type": "https://api.myservice.com/errors/user-not-found",
"title": "Could not find user with id 123"
}
Note how we could use lower-snake-case to have the generated URI look nice.
Another example:
{
"code": "VALIDATION_FAILED",
"message": "Validation failed for object='exampleRequestBody'. Error count: 2",
"fieldErrors": [
{
"code": "INVALID_SIZE",
"property": "name",
"message": "size must be between 10 and 2147483647",
"rejectedValue": "",
"path": "name"
},
{
"code": "REQUIRED_NOT_BLANK",
"property": "favoriteMovie",
"message": "must not be blank",
"rejectedValue": null,
"path": "favoriteMovie"
}
]
}
Would become:
{
"type": "https://api.myservice.com/errors/validation-failed",
"title": "Validation failed for object='exampleRequestBody'. Error count: 2",
"fieldErrors": [
{
"code": "INVALID_SIZE",
"property": "name",
"message": "size must be between 10 and 2147483647",
"rejectedValue": "",
"path": "name"
},
{
"code": "REQUIRED_NOT_BLANK",
"property": "favoriteMovie",
"message": "must not be blank",
"rejectedValue": null,
"path": "favoriteMovie"
}
]
}
"code": "USER_NOT_FOUND",
We can achieve the same using problemDetail.setProperty("code", "USER_NOT_FOUND")
so migration will be easier as we are retaining earlier key name
The specification states:
Consumers MUST use the "type" string as the primary identifier for the problem type
So type
is what code
has always been for this library. A machine-readable string for consumers of the API to know the problem type.
I don't see any advantage of supporting ProblemDetail if we don't adhere to the specification.
It seems like type
is an optional field?
When this member is not present, its value is assumed to be about:blank".
And it also states
The "about:blank" URI [RFC6694], when used as a problem type, indicates that the problem has no additional semantics beyond that of the HTTP status code.
When "about:blank" is used, the title SHOULD be the same as the recommended HTTP status phrase for that code (e.g., "Not Found" for 404, and so on), although it MAY be localized to suit client preferences (expressed with the Accept-Language request header).
Would that then be
{
"title": "BAD_REQUEST",
"fieldErrors": [
{
"code": "INVALID_SIZE",
"property": "name",
"message": "size must be between 10 and 2147483647",
"rejectedValue": "",
"path": "name"
},
{
"code": "REQUIRED_NOT_BLANK",
"property": "favoriteMovie",
"message": "must not be blank",
"rejectedValue": null,
"path": "favoriteMovie"
}
]
}
For reference, or for people how need problem support now, there is another library: https://github.com/zalando/problem-spring-web which configures https://github.com/zalando/problem for Spring Boot.
Yes, the type is optional. When absent or "about:blank", it doesn't say anything about the shape of the problem JSON, and "indicates that the problem has no additional semantics beyond that of the HTTP status code." so that means that the JSON should just look something like
{
"title": "Not Found",
"status": 404
}
In practice, it still works fine when additional fields are provided, like
{
"title": "Not Found",
"status": 404,
"detail": "Enity foo id=1 not found"
}
IMO, the closest thing to your code
is the problem title
,
"title" (string) - A short, human-readable summary of the problem type. It SHOULD NOT change from occurrence to occurrence of the problem, except for purposes of localization ...
When the type
is present, it should be a URI that defines and explains the shape of the problem JSON. For example, the Zalando library uses this URL https://opensource.zalando.com/problem/constraint-violation/ as type for constraint violations.
Tl;dr: to support the problem RFC, this library could
- use
title
instead ofcode
- use
detail
instead ofmessage
- simply omit the
type
, or define types to describe the default and the validation error response shapes, or leave it up to the user to define a type with an annotation. I guess the latter would already be possible with@ResponseErrorProperty
.