Skip to content

Commit 61eaa93

Browse files
committed
Polishing in ErrorResponse
See gh-30566
1 parent 9c7b5cb commit 61eaa93

File tree

3 files changed

+78
-78
lines changed

3 files changed

+78
-78
lines changed

spring-web/src/main/java/org/springframework/web/ErrorResponse.java

Lines changed: 48 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ default HttpHeaders getHeaders() {
6464
*/
6565
ProblemDetail getBody();
6666

67+
68+
// MessageSource codes and arguments
69+
6770
/**
6871
* Return a code to use to resolve the problem "type" for this exception
6972
* through a {@link MessageSource}. The type resolved through the
@@ -76,6 +79,15 @@ default String getTypeMessageCode() {
7679
return getDefaultTypeMessageCode(getClass());
7780
}
7881

82+
/**
83+
* Return a code to use to resolve the problem "title" for this exception
84+
* through a {@link MessageSource}.
85+
* <p>By default this is initialized via {@link #getDefaultTitleMessageCode(Class)}.
86+
*/
87+
default String getTitleMessageCode() {
88+
return getDefaultTitleMessageCode(getClass());
89+
}
90+
7991
/**
8092
* Return a code to use to resolve the problem "detail" for this exception
8193
* through a {@link MessageSource}.
@@ -111,15 +123,6 @@ default Object[] getDetailMessageArguments(MessageSource messageSource, Locale l
111123
return getDetailMessageArguments();
112124
}
113125

114-
/**
115-
* Return a code to use to resolve the problem "title" for this exception
116-
* through a {@link MessageSource}.
117-
* <p>By default this is initialized via {@link #getDefaultTitleMessageCode(Class)}.
118-
*/
119-
default String getTitleMessageCode() {
120-
return getDefaultTitleMessageCode(getClass());
121-
}
122-
123126
/**
124127
* Use the given {@link MessageSource} to resolve the
125128
* {@link #getTypeMessageCode() type}, {@link #getTitleMessageCode() title},
@@ -159,6 +162,16 @@ static String getDefaultTypeMessageCode(Class<?> exceptionType) {
159162
return "problemDetail.type." + exceptionType.getName();
160163
}
161164

165+
/**
166+
* Build a message code for the "title" field, for the given exception type.
167+
* @param exceptionType the exception type associated with the problem
168+
* @return {@code "problemDetail.title."} followed by the fully qualified
169+
* {@link Class#getName() class name}
170+
*/
171+
static String getDefaultTitleMessageCode(Class<?> exceptionType) {
172+
return "problemDetail.title." + exceptionType.getName();
173+
}
174+
162175
/**
163176
* Build a message code for the "detail" field, for the given exception type.
164177
* @param exceptionType the exception type associated with the problem
@@ -171,15 +184,6 @@ static String getDefaultDetailMessageCode(Class<?> exceptionType, @Nullable Stri
171184
return "problemDetail." + exceptionType.getName() + (suffix != null ? "." + suffix : "");
172185
}
173186

174-
/**
175-
* Build a message code for the "title" field, for the given exception type.
176-
* @param exceptionType the exception type associated with the problem
177-
* @return {@code "problemDetail.title."} followed by the fully qualified
178-
* {@link Class#getName() class name}
179-
*/
180-
static String getDefaultTitleMessageCode(Class<?> exceptionType) {
181-
return "problemDetail.title." + exceptionType.getName();
182-
}
183187

184188
/**
185189
* Static factory method to build an instance via
@@ -230,61 +234,61 @@ interface Builder {
230234
Builder headers(Consumer<HttpHeaders> headersConsumer);
231235

232236
/**
233-
* Set the underlying {@link ProblemDetail#setDetail(String) detail}.
237+
* Set the underlying {@link ProblemDetail#setType(URI) type} field.
234238
* @return the same builder instance
235239
*/
236-
Builder detail(String detail);
240+
Builder type(URI type);
237241

238242
/**
239-
* Customize the {@link MessageSource} code for looking up the value for
240-
* the underlying {@link #detail(String) detail}.
241-
* <p>By default, this is set to
242-
* {@link ErrorResponse#getDefaultDetailMessageCode(Class, String)} with the
243-
* associated Exception type.
244-
* @param messageCode the message code to use
243+
* Set the underlying {@link ProblemDetail#setTitle(String) title} field.
245244
* @return the same builder instance
246-
* @see ErrorResponse#getDetailMessageCode()
247245
*/
248-
Builder detailMessageCode(String messageCode);
246+
Builder title(@Nullable String title);
249247

250248
/**
251-
* Set the arguments to provide to the {@link MessageSource} lookup for
252-
* {@link #detailMessageCode(String)}.
253-
* @param messageArguments the arguments to provide
249+
* Customize the {@link MessageSource} code for looking up the value for
250+
* the underlying {@link ProblemDetail#setTitle(String) title}.
251+
* <p>By default, set via
252+
* {@link ErrorResponse#getDefaultTitleMessageCode(Class)} with the
253+
* associated Exception type.
254+
* @param messageCode the message code to use
254255
* @return the same builder instance
255-
* @see ErrorResponse#getDetailMessageArguments()
256+
* @see ErrorResponse#getTitleMessageCode()
256257
*/
257-
Builder detailMessageArguments(Object... messageArguments);
258+
Builder titleMessageCode(String messageCode);
258259

259260
/**
260-
* Set the underlying {@link ProblemDetail#setType(URI) type} field.
261+
* Set the underlying {@link ProblemDetail#setInstance(URI) instance} field.
261262
* @return the same builder instance
262263
*/
263-
Builder type(URI type);
264+
Builder instance(@Nullable URI instance);
264265

265266
/**
266-
* Set the underlying {@link ProblemDetail#setTitle(String) title} field.
267+
* Set the underlying {@link ProblemDetail#setDetail(String) detail}.
267268
* @return the same builder instance
268269
*/
269-
Builder title(@Nullable String title);
270+
Builder detail(String detail);
270271

271272
/**
272273
* Customize the {@link MessageSource} code for looking up the value for
273-
* the underlying {@link ProblemDetail#setTitle(String) title}.
274-
* <p>By default, set via
275-
* {@link ErrorResponse#getDefaultTitleMessageCode(Class)} with the
274+
* the underlying {@link #detail(String) detail}.
275+
* <p>By default, this is set to
276+
* {@link ErrorResponse#getDefaultDetailMessageCode(Class, String)} with the
276277
* associated Exception type.
277278
* @param messageCode the message code to use
278279
* @return the same builder instance
279-
* @see ErrorResponse#getTitleMessageCode()
280+
* @see ErrorResponse#getDetailMessageCode()
280281
*/
281-
Builder titleMessageCode(String messageCode);
282+
Builder detailMessageCode(String messageCode);
282283

283284
/**
284-
* Set the underlying {@link ProblemDetail#setInstance(URI) instance} field.
285+
* Set the arguments to provide to the {@link MessageSource} lookup for
286+
* {@link #detailMessageCode(String)}.
287+
* @param messageArguments the arguments to provide
285288
* @return the same builder instance
289+
* @see ErrorResponse#getDetailMessageArguments()
286290
*/
287-
Builder instance(@Nullable URI instance);
291+
Builder detailMessageArguments(Object... messageArguments);
288292

289293
/**
290294
* Set a "dynamic" {@link ProblemDetail#setProperty(String, Object)

spring-webflux/src/test/java/org/springframework/web/reactive/result/method/annotation/ResponseEntityExceptionHandlerTests.java

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -127,37 +127,35 @@ void handleErrorResponseException() {
127127

128128
@Test
129129
void errorResponseProblemDetailViaMessageSource() {
130-
131130
Locale locale = Locale.UK;
132-
LocaleContextHolder.setLocale(locale);
133131

134132
String type = "https://example.com/probs/unsupported-content";
135133
String title = "Media type is not valid or not supported";
134+
Class<UnsupportedMediaTypeStatusException> exceptionType = UnsupportedMediaTypeStatusException.class;
136135

137-
StaticMessageSource messageSource = new StaticMessageSource();
138-
messageSource.addMessage(
139-
ErrorResponse.getDefaultDetailMessageCode(UnsupportedMediaTypeStatusException.class, null), locale,
136+
StaticMessageSource source = new StaticMessageSource();
137+
source.addMessage(ErrorResponse.getDefaultTypeMessageCode(exceptionType), locale, type);
138+
source.addMessage(ErrorResponse.getDefaultTitleMessageCode(exceptionType), locale, title);
139+
source.addMessage(ErrorResponse.getDefaultDetailMessageCode(exceptionType, null), locale,
140140
"Content-Type {0} not supported. Supported: {1}");
141-
messageSource.addMessage(
142-
ErrorResponse.getDefaultTitleMessageCode(UnsupportedMediaTypeStatusException.class), locale, title);
143-
messageSource.addMessage(
144-
ErrorResponse.getDefaultTypeMessageCode(UnsupportedMediaTypeStatusException.class), locale, type);
145141

146-
this.exceptionHandler.setMessageSource(messageSource);
142+
this.exceptionHandler.setMessageSource(source);
147143

148-
Exception ex = new UnsupportedMediaTypeStatusException(MediaType.APPLICATION_JSON,
149-
List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML));
144+
Exception ex = new UnsupportedMediaTypeStatusException(
145+
MediaType.APPLICATION_JSON, List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML));
150146

151147
MockServerWebExchange exchange = MockServerWebExchange.from(
152148
MockServerHttpRequest.get("/").acceptLanguageAsLocales(locale).build());
153149

154150
ResponseEntity<?> responseEntity = this.exceptionHandler.handleException(ex, exchange).block();
151+
assertThat(responseEntity).isNotNull();
155152

156-
ProblemDetail body = (ProblemDetail) responseEntity.getBody();
157-
assertThat(body.getDetail()).isEqualTo(
153+
ProblemDetail problem = (ProblemDetail) responseEntity.getBody();
154+
assertThat(problem).isNotNull();
155+
assertThat(problem.getType()).isEqualTo(URI.create(type));
156+
assertThat(problem.getTitle()).isEqualTo(title);
157+
assertThat(problem.getDetail()).isEqualTo(
158158
"Content-Type application/json not supported. Supported: [application/atom+xml, application/xml]");
159-
assertThat(body.getTitle()).isEqualTo(title);
160-
assertThat(body.getType()).isEqualTo(URI.create(type));
161159
}
162160

163161
@Test

spring-webmvc/src/test/java/org/springframework/web/servlet/mvc/method/annotation/ResponseEntityExceptionHandlerTests.java

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,33 +161,31 @@ public void servletRequestBindingException() {
161161

162162
@Test
163163
public void errorResponseProblemDetailViaMessageSource() {
164+
try {
165+
Locale locale = Locale.UK;
166+
LocaleContextHolder.setLocale(locale);
164167

165-
Locale locale = Locale.UK;
166-
LocaleContextHolder.setLocale(locale);
167-
168-
String type = "https://example.com/probs/unsupported-content";
169-
String title = "Media type is not valid or not supported";
168+
String type = "https://example.com/probs/unsupported-content";
169+
String title = "Media type is not valid or not supported";
170+
Class<HttpMediaTypeNotSupportedException> exceptionType = HttpMediaTypeNotSupportedException.class;
170171

171-
try {
172-
StaticMessageSource messageSource = new StaticMessageSource();
173-
messageSource.addMessage(
174-
ErrorResponse.getDefaultDetailMessageCode(HttpMediaTypeNotSupportedException.class, null), locale,
172+
StaticMessageSource source = new StaticMessageSource();
173+
source.addMessage(ErrorResponse.getDefaultTypeMessageCode(exceptionType), locale, type);
174+
source.addMessage(ErrorResponse.getDefaultTitleMessageCode(exceptionType), locale, title);
175+
source.addMessage(ErrorResponse.getDefaultDetailMessageCode(exceptionType, null), locale,
175176
"Content-Type {0} not supported. Supported: {1}");
176-
messageSource.addMessage(
177-
ErrorResponse.getDefaultTitleMessageCode(HttpMediaTypeNotSupportedException.class), locale, title);
178-
messageSource.addMessage(
179-
ErrorResponse.getDefaultTypeMessageCode(HttpMediaTypeNotSupportedException.class), locale, type);
180177

181-
this.exceptionHandler.setMessageSource(messageSource);
178+
this.exceptionHandler.setMessageSource(source);
182179

183180
ResponseEntity<?> entity = testException(new HttpMediaTypeNotSupportedException(
184181
MediaType.APPLICATION_JSON, List.of(MediaType.APPLICATION_ATOM_XML, MediaType.APPLICATION_XML)));
185182

186-
ProblemDetail body = (ProblemDetail) entity.getBody();
187-
assertThat(body.getDetail()).isEqualTo(
183+
ProblemDetail problem = (ProblemDetail) entity.getBody();
184+
assertThat(problem).isNotNull();
185+
assertThat(problem.getType()).isEqualTo(URI.create(type));
186+
assertThat(problem.getTitle()).isEqualTo(title);
187+
assertThat(problem.getDetail()).isEqualTo(
188188
"Content-Type application/json not supported. Supported: [application/atom+xml, application/xml]");
189-
assertThat(body.getTitle()).isEqualTo(title);
190-
assertThat(body.getType()).isEqualTo(URI.create(type));
191189
}
192190
finally {
193191
LocaleContextHolder.resetLocaleContext();

0 commit comments

Comments
 (0)