Skip to content

Commit c7386d2

Browse files
committed
Update README.md
1 parent 09201fc commit c7386d2

File tree

1 file changed

+112
-226
lines changed

1 file changed

+112
-226
lines changed

problem4j-spring-web/README.md

Lines changed: 112 additions & 226 deletions
Original file line numberDiff line numberDiff line change
@@ -12,233 +12,100 @@ configuration class.
1212
Overriding whole `ProblemEnhancedExceptionHandler` is not recommended, although such necessities are sometimes
1313
understandable.
1414

15-
## `AsyncRequestTimeoutException`
15+
## Occurrences of `ProblemException`
1616

17-
What triggers it: An async request (e.g. `DeferredResult`, `Callable`, WebAsync) exceeded configured timeout before
18-
producing a value.
19-
20-
Mapping: [`AsyncRequestTimeoutMapping`][AsyncRequestTimeoutMapping]
21-
22-
Example:
23-
24-
```json
25-
{
26-
"status": 500,
27-
"title": "Internal Server Error"
28-
}
29-
```
30-
31-
## `ConversionNotSupportedException`
32-
33-
What triggers it: Spring's type conversion system could not convert a controller method return value or property to the
34-
required type (fatal conversion issue, not just a simple mismatch).
35-
36-
Mapping: [`ConversionNotSupportedMapping`][ConversionNotSupportedMapping]
37-
38-
Example:
39-
40-
```json
41-
{
42-
"status": 500,
43-
"title": "Internal Server Error"
44-
}
45-
```
46-
47-
## `ErrorResponseException`
48-
49-
What triggers it: Explicitly thrown `ErrorResponseException` (or subclasses like `ResponseStatusException`). Each of
50-
these exceptions carry HTTP status within it as well as details to be used in `application/problem+json` response.
51-
52-
Mapping: [`ErrorResponseMapping`][ErrorResponseMapping]
53-
54-
Example:
17+
Explicitly thrown `ProblemException` (or subclasses created by its users). Each of these exceptions carry HTTP status
18+
within it as well as details to be used in `application/problem+json` response.
5519

5620
```json
5721
{
5822
"type": "https://example.org/problem-type",
5923
"title": "Some Error",
6024
"status": 400,
6125
"detail": "Explanation of the error",
62-
"instance": "https://example.org/instances/123",
63-
"extraKey": "extraValue"
64-
}
65-
```
66-
67-
## `HttpMediaTypeNotAcceptableException`
68-
69-
What triggers it: Client `Accept` header doesn't match any producible media type from controller.
70-
71-
Mapping: [`HttpMediaTypeNotAcceptableMapping`][HttpMediaTypeNotAcceptableMapping]
72-
73-
```json
74-
{
75-
"status": 406,
76-
"title": "Not Acceptable"
77-
}
78-
```
79-
80-
## `HttpMediaTypeNotSupportedException`
81-
82-
What triggers it: Request `Content-Type` not supported by any `HttpMessageConverter` for the target endpoint.
83-
84-
Mapping: [`HttpMediaTypeNotSupportedMapping`][HttpMediaTypeNotSupportedMapping]
85-
86-
```json
87-
{
88-
"status": 415,
89-
"title": "Unsupported Media Type"
90-
}
91-
```
92-
93-
## `HttpMessageNotReadableException`
94-
95-
What triggers it: Incoming request body couldn't be parsed/deserialized (malformed JSON, wrong structure, EOF, etc.).
96-
97-
Mapping: [`HttpMessageNotReadableMapping`][HttpMessageNotReadableMapping]
98-
99-
```json
100-
{
101-
"status": 400,
102-
"title": "Bad Request"
103-
}
104-
```
105-
106-
## `HttpMessageNotWritableException`
107-
108-
What triggers it: Server failed to serialize the response body (e.g. Jackson serialization problem) after controller
109-
returned a value.
110-
111-
Mapping: [`HttpMessageNotWritableMapping`][HttpMessageNotWritableMapping]
112-
113-
```json
114-
{
115-
"status": 500,
116-
"title": "Internal Server Error"
117-
}
118-
```
119-
120-
## `HttpRequestMethodNotSupportedException`
121-
122-
What triggers it: HTTP method not supported for a particular endpoint (e.g. `POST` to an endpoint allowing only `GET`).
123-
124-
Mapping: [`HttpRequestMethodNotSupportedMapping`][HttpRequestMethodNotSupportedMapping]
125-
126-
```json
127-
{
128-
"status": 405,
129-
"title": "Method Not Allowed"
130-
}
131-
```
132-
133-
## `MaxUploadSizeExceededException`
134-
135-
What triggers it: Multipart upload exceeds configured max file or request size.
136-
137-
Mapping: [`MaxUploadSizeExceededMapping`][MaxUploadSizeExceededMapping]
138-
139-
```json
140-
{
141-
"status": 413,
142-
"title": "Content Too Large",
143-
"detail": "Max upload size exceeded",
144-
"max": 1048576
26+
"instance": "https://example.org/instances/123"
14527
}
14628
```
14729

148-
## `MethodArgumentNotValidException`
30+
**Note** that the main reason behind this project is to make `ProblemException` a base class for all custom exception in
31+
your application code.
14932

150-
What triggers it: Bean Validation (JSR 380) failed for a `@Valid` annotated argument (e.g. request body DTO) during data
151-
binding.
33+
## Validation
15234

153-
Mapping: [`MethodArgumentNotValidMapping`][MethodArgumentNotValidMapping]
35+
Library overrides default responses for `jakarta.validation` exceptions for both `@RequestBody` and any other
36+
`@RestController` arguments.
15437

15538
```json
15639
{
15740
"status": 400,
15841
"title": "Bad Request",
15942
"detail": "Validation failed",
160-
"errors": [
161-
{
162-
"field": "email",
163-
"error": "must be a well-formed email address"
164-
},
165-
{
166-
"field": "age",
167-
"error": "must be greater than or equal to 18"
168-
}
169-
]
43+
"errors": [ {
44+
"field": "email",
45+
"error": "must be a well-formed email address"
46+
}, {
47+
"field": "age",
48+
"error": "must be greater than or equal to 18"
49+
} ]
17050
}
17151
```
17252

173-
Field names convention may be formatted (e.g. `snake_case`) by configuring `spring.jackson.property-naming-strategy`.
174-
175-
## `MissingPathVariableException`
176-
177-
What triggers it: A required URI template variable was not provided (e.g. handler expected `{id}` path variable that was
178-
absent in request mapping resolution).
53+
More notably, for `@RequestParam`, `@RequestHeader` etc., there's a tweak that comes from settings configuration
54+
property `spring.validation.method.adapt-constraint-violations` to `true`. Enabling it, switches default validation to
55+
not rely on raw `ConstraintViolationException`, but rather on `MethodValidationException`, which contains more details
56+
about validated element.
17957

180-
Mapping: [`MissingPathVariableMapping`][MissingPathVariableMapping]
58+
Let's say we have following `@RestController`, where `idx` query param has different Java parameter name.
18159

182-
```json
183-
{
184-
"status": 400,
185-
"title": "Bad Request",
186-
"detail": "Missing path variable",
187-
"name": "id"
60+
```java
61+
@Validated
62+
@RestController
63+
static class RequestParamController {
64+
@GetMapping("/orders")
65+
String endpoint(@RequestParam("customerId") @Size(min = 5, max = 30) String customerIdParam) {
66+
return "OK";
67+
}
18868
}
18969
```
19070

191-
## `MissingServletRequestParameterException`
71+
The `errors.$.field` will differ, depending on whether `spring.validation.method.adapt-constraint-violations` is enabled
72+
or not. For `true` it will use value from `@RequestParam` (if able) (the same goes for `@PathVariable`,
73+
`@RequestHeader`, `@CookieValue` etc.).
19274

193-
What triggers it: Required query parameter is missing (e.g. `@RequestParam(required=true)` not supplied by client).
194-
195-
Mapping: [`MissingServletRequestParameterMapping`][MissingServletRequestParameterMapping]
196-
197-
```json
75+
<table>
76+
<tr>
77+
<td align="center"><code>ConstraintViolationException</code></td>
78+
<td align="center"><code>MethodValidationException</code></td>
79+
</tr>
80+
<tr>
81+
<td><pre lang="json">
19882
{
19983
"status": 400,
20084
"title": "Bad Request",
201-
"detail": "Missing request param",
202-
"param": "q",
203-
"kind": "string"
85+
"detail": "Validation failed",
86+
"errors": [ {
87+
"field": "customerIdParam",
88+
"error": "size must be between 5 and 30"
89+
} ]
20490
}
205-
```
206-
207-
## `MissingServletRequestPartException`
208-
209-
What triggers it: Required multipart request part missing (e.g. file field in a multipart/form-data POST not provided).
210-
211-
Mapping: [`MissingServletRequestPartMapping`][MissingServletRequestPartMapping]
212-
213-
```json
91+
</pre></td>
92+
<td><pre lang="json">
21493
{
21594
"status": 400,
21695
"title": "Bad Request",
217-
"detail": "Missing request part",
218-
"param": "file"
219-
}
220-
```
221-
222-
## `ServletRequestBindingException`
223-
224-
What triggers it: General binding issues with request parameters, headers, path variables (e.g. missing header required
225-
by `@RequestHeader`).
226-
227-
Mapping: [`ServletRequestBindingMapping`][ServletRequestBindingMapping]
228-
229-
```json
230-
{
231-
"status": 400,
232-
"title": "Bad Request"
96+
"detail": "Validation failed",
97+
"errors": [ {
98+
"field": "customerId",
99+
"error": "size must be between 5 and 30"
100+
} ]
233101
}
234-
```
102+
</pre></td>
103+
</tr>
104+
</table>
235105

236-
## `TypeMismatchException`
106+
## Occurrences of `TypeMismatchException`
237107

238-
What triggers it: Failed to bind a web request parameter/path variable to a controller argument due to type mismatch (
239-
e.g. `age=abc` where `age` expects an integer).
240-
241-
Mapping: [`TypeMismatchMapping`][TypeMismatchMapping]
108+
Triggered for example when trying to pass `String` value into `@RequestParam("param") Integer param`.
242109

243110
```json
244111
{
@@ -250,48 +117,67 @@ Mapping: [`TypeMismatchMapping`][TypeMismatchMapping]
250117
}
251118
```
252119

253-
## Fallback / Unknown Exceptions
120+
## Occurrences of `ErrorResponseException`
121+
122+
Similar to `ProblemException`, but comes from Spring and relies on mutable `ProblemDetails` object.
254123

255-
What triggers it: Any unhandled exception flowing through `ResponseEntityExceptionHandler` without a dedicated mapping.
256-
Result example:
124+
Explicitly thrown `ErrorResponseException` (or subclasses like `ResponseStatusException`). Each of these exceptions
125+
carry HTTP status within it as well as details to be used in `application/problem+json` response.
126+
127+
Example:
257128

258129
```json
259130
{
260-
"status": 500,
261-
"title": "Internal Server Error"
262-
}
263-
```
131+
"type": "https://example.org/problem-type",
132+
"title": "Some Error",
133+
"status": 400,
134+
"detail": "Explanation of the error",
135+
"instance": "https://example.org/instances/123"
136+
}
137+
```
138+
139+
## General HTTP Stuff
140+
141+
1. If trying to call `POST` for and endpoint with only `GET` (or any other similar situation), service will write
142+
following response.
143+
```json
144+
{
145+
"status": 405,
146+
"title": "Method Not Allowed"
147+
}
148+
```
149+
2. If calling REST API with invalid `Accept` header, service will write following response.
150+
```json
151+
{
152+
"status": 406,
153+
"title": "Not Acceptable"
154+
}
155+
```
156+
3. If calling REST API with invalid `Content-Type` header, service will write following response.
157+
```json
158+
{
159+
"status": 415,
160+
"title": "Unsupported Media Type"
161+
}
162+
```
163+
4. If passing request body that has invalid JSON syntax, service will write following response.
164+
```json
165+
{
166+
"status": 400,
167+
"title": "Bad Request"
168+
}
169+
```
170+
5. If passing request that's too large by configuration, service will write following response. Note that reason phrase
171+
for `413` was changed into `Content Too Large` in [RFC 9110 §15.5.14][rfc9110-15.5.4].
172+
```json
173+
{
174+
"status": 413,
175+
"title": "Content Too Large"
176+
}
177+
```
178+
179+
[rfc9110-15.5.4]: https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.14
264180

265181
[ExceptionMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/ExceptionMapping.java
266182

267183
[ExceptionMappingConfiguration]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/ExceptionMappingConfiguration.java
268-
269-
[AsyncRequestTimeoutMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/AsyncRequestTimeoutMapping.java
270-
271-
[ConversionNotSupportedMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/ConversionNotSupportedMapping.java
272-
273-
[ErrorResponseMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/ErrorResponseMapping.java
274-
275-
[HttpMediaTypeNotAcceptableMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/HttpMediaTypeNotAcceptableMapping.java
276-
277-
[HttpMediaTypeNotSupportedMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/HttpMediaTypeNotSupportedMapping.java
278-
279-
[HttpMessageNotReadableMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/HttpMessageNotReadableMapping.java
280-
281-
[HttpMessageNotWritableMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/HttpMessageNotWritableMapping.java
282-
283-
[HttpRequestMethodNotSupportedMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/HttpRequestMethodNotSupportedMapping.java
284-
285-
[MaxUploadSizeExceededMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/MaxUploadSizeExceededMapping.java
286-
287-
[MethodArgumentNotValidMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/MethodArgumentNotValidMapping.java
288-
289-
[MissingPathVariableMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/MissingPathVariableMapping.java
290-
291-
[MissingServletRequestParameterMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/MissingServletRequestParameterMapping.java
292-
293-
[MissingServletRequestPartMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/MissingServletRequestPartMapping.java
294-
295-
[ServletRequestBindingMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/ServletRequestBindingMapping.java
296-
297-
[TypeMismatchMapping]: src/main/java/io/github/malczuuu/problem4j/spring/web/mapping/TypeMismatchMapping.java

0 commit comments

Comments
 (0)