Support content negotiation for RFC 7807

Closes gh-28189
This commit is contained in:
rstoyanchev
2022-05-09 18:31:47 +01:00
parent f3fd8f9e1d
commit 78ab4d7118
10 changed files with 228 additions and 33 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
@@ -38,6 +38,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.core.codec.Hints;
import org.springframework.http.HttpLogging;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.lang.Nullable;
@@ -89,6 +90,8 @@ public abstract class Jackson2CodecSupport {
private final List<MimeType> mimeTypes;
private final List<MimeType> problemDetailMimeTypes;
/**
* Constructor with a Jackson {@link ObjectMapper} to use.
@@ -96,8 +99,15 @@ 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.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);
}
@@ -180,7 +190,10 @@ public abstract class Jackson2CodecSupport {
result.addAll(entry.getValue().keySet());
}
}
return (CollectionUtils.isEmpty(result) ? getMimeTypes() : result);
if (!CollectionUtils.isEmpty(result)) {
return result;
}
return (ProblemDetail.class.isAssignableFrom(elementClass) ? this.problemDetailMimeTypes : getMimeTypes());
}
protected boolean supportsMimeType(@Nullable MimeType mimeType) {

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 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.
@@ -100,12 +100,12 @@ public abstract class AbstractHttpMessageConverter<T> implements HttpMessageConv
*/
public void setSupportedMediaTypes(List<MediaType> supportedMediaTypes) {
Assert.notEmpty(supportedMediaTypes, "MediaType List must not be empty");
this.supportedMediaTypes = new ArrayList<>(supportedMediaTypes);
this.supportedMediaTypes = Collections.unmodifiableList(new ArrayList<>(supportedMediaTypes));
}
@Override
public List<MediaType> getSupportedMediaTypes() {
return Collections.unmodifiableList(this.supportedMediaTypes);
return this.supportedMediaTypes;
}
/**

View File

@@ -53,6 +53,7 @@ import org.springframework.core.GenericTypeResolver;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.ProblemDetail;
import org.springframework.http.converter.AbstractGenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConversionException;
import org.springframework.http.converter.HttpMessageConverter;
@@ -92,6 +93,8 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
}
private List<MediaType> problemDetailMediaTypes = Collections.singletonList(MediaType.APPLICATION_PROBLEM_JSON);
protected ObjectMapper defaultObjectMapper;
@Nullable
@@ -122,6 +125,19 @@ 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.
@@ -198,7 +214,11 @@ public abstract class AbstractJackson2HttpMessageConverter extends AbstractGener
result.addAll(entry.getValue().keySet());
}
}
return (CollectionUtils.isEmpty(result) ? getSupportedMediaTypes() : result);
if (!CollectionUtils.isEmpty(result)) {
return result;
}
return (ProblemDetail.class.isAssignableFrom(clazz) ?
this.problemDetailMediaTypes : getSupportedMediaTypes());
}
private Map<Class<?>, Map<MediaType, ObjectMapper>> getObjectMapperRegistrations() {