Always use application/problem+json with ProblemDetail

See gh-gh-29588
This commit is contained in:
rstoyanchev 2023-02-06 14:10:58 +00:00
parent 7851994a17
commit ce85fdc5c7
4 changed files with 24 additions and 29 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -75,11 +75,14 @@ public abstract class Jackson2CodecSupport {
private static final String JSON_VIEW_HINT_ERROR =
"@JsonView only supported for write hints with exactly 1 class argument: ";
private static final List<MimeType> DEFAULT_MIME_TYPES = List.of(
private static final List<MimeType> defaultMimeTypes = List.of(
MediaType.APPLICATION_JSON,
new MediaType("application", "*+json"),
MediaType.APPLICATION_NDJSON);
private static final List<MimeType> problemDetailMimeTypes =
Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON);
protected final Log logger = HttpLogging.forLogName(getClass());
@ -90,8 +93,6 @@ public abstract class Jackson2CodecSupport {
private final List<MimeType> mimeTypes;
private final List<MimeType> problemDetailMimeTypes;
/**
* Constructor with a Jackson {@link ObjectMapper} to use.
@ -99,15 +100,7 @@ public abstract class Jackson2CodecSupport {
protected Jackson2CodecSupport(ObjectMapper objectMapper, MimeType... mimeTypes) {
Assert.notNull(objectMapper, "ObjectMapper must not be null");
this.defaultObjectMapper = objectMapper;
this.mimeTypes = (!ObjectUtils.isEmpty(mimeTypes) ? List.of(mimeTypes) : DEFAULT_MIME_TYPES);
this.problemDetailMimeTypes = initProblemDetailMediaTypes(this.mimeTypes);
}
private static List<MimeType> initProblemDetailMediaTypes(List<MimeType> supportedMimeTypes) {
List<MimeType> mimeTypes = new ArrayList<>();
mimeTypes.add(MediaType.APPLICATION_PROBLEM_JSON);
mimeTypes.addAll(supportedMimeTypes);
return Collections.unmodifiableList(mimeTypes);
this.mimeTypes = (!ObjectUtils.isEmpty(mimeTypes) ? List.of(mimeTypes) : defaultMimeTypes);
}
@ -193,7 +186,7 @@ public abstract class Jackson2CodecSupport {
if (!CollectionUtils.isEmpty(result)) {
return result;
}
return (ProblemDetail.class.isAssignableFrom(elementClass) ? this.problemDetailMimeTypes : getMimeTypes());
return (ProblemDetail.class.isAssignableFrom(elementClass) ? problemDetailMimeTypes : getMimeTypes());
}
protected boolean supportsMimeType(@Nullable MimeType mimeType) {

View File

@ -91,8 +91,9 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
ENCODINGS.put("US-ASCII", JsonEncoding.UTF8);
}
private static final List<MediaType> problemDetailMediaTypes =
Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON);
private List<MediaType> problemDetailMediaTypes = Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON);
protected ObjectMapper defaultObjectMapper;
@ -126,17 +127,9 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
@Override
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
this.problemDetailMediaTypes = initProblemDetailMediaTypes(supportedMediaTypes);
super.setSupportedMediaTypes(supportedMediaTypes);
}
private List<MediaType> initProblemDetailMediaTypes(List<MediaType> supportedMediaTypes) {
List<MediaType> mediaTypes = new ArrayList<>();
mediaTypes.add(MediaType.APPLICATION_PROBLEM_JSON);
mediaTypes.addAll(supportedMediaTypes);
return Collections.unmodifiableList(mediaTypes);
}
/**
* Configure the main {@code ObjectMapper} to use for Object conversion.
* If not set, a default {@link ObjectMapper} instance is created.
@ -216,8 +209,7 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
if (!CollectionUtils.isEmpty(result)) {
return result;
}
return (ProblemDetail.class.isAssignableFrom(clazz) ?
this.problemDetailMediaTypes : getSupportedMediaTypes());
return (ProblemDetail.class.isAssignableFrom(clazz) ? problemDetailMediaTypes : getSupportedMediaTypes());
}
private Map<Class<?>, Map<MediaType, ObjectMapper>> getObjectMapperRegistrations() {

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -127,7 +127,11 @@ public class ResponseBodyResultHandlerTests {
// JSON requested
exchange = MockServerWebExchange.from(get("/path").accept(MediaType.APPLICATION_JSON));
testProblemDetailMediaType(exchange, MediaType.APPLICATION_JSON);
testProblemDetailMediaType(exchange, MediaType.APPLICATION_PROBLEM_JSON);
// JSON & Problem Detail requested (gh-29588)
exchange = MockServerWebExchange.from(get("/path").accept(MediaType.APPLICATION_JSON, MediaType.APPLICATION_PROBLEM_JSON));
testProblemDetailMediaType(exchange, MediaType.APPLICATION_PROBLEM_JSON);
// No match fallback
exchange = MockServerWebExchange.from(get("/path").accept(MediaType.APPLICATION_PDF));

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2022 the original author or authors.
* Copyright 2002-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -403,7 +403,13 @@ public class RequestResponseBodyMethodProcessorTests {
@Test
void problemDetailWhenJsonRequested() throws Exception {
this.servletRequest.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE);
testProblemDetailMediaType(MediaType.APPLICATION_JSON_VALUE);
testProblemDetailMediaType(MediaType.APPLICATION_PROBLEM_JSON_VALUE);
}
@Test // gh-29588
void problemDetailWhenJsonAndProblemJsonRequested() throws Exception {
this.servletRequest.addHeader("Accept", MediaType.APPLICATION_JSON_VALUE + "," + MediaType.APPLICATION_PROBLEM_JSON_VALUE);
testProblemDetailMediaType(MediaType.APPLICATION_PROBLEM_JSON_VALUE);
}
@Test