Introduce hints support in advices
This commit introduces RequestBodyAdvice#determineReadHints and ResponseBodyAdvice#determineWriteHints in order to be able to support SmartHttpMessageConverter hints, as well as related `@JsonView` support. See gh-33798
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2023 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -20,6 +20,7 @@ import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonValue;
|
||||
@@ -39,7 +40,8 @@ public abstract class AbstractMappingJacksonResponseBodyAdvice implements Respon
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
|
||||
return AbstractJacksonHttpMessageConverter.class.isAssignableFrom(converterType) ||
|
||||
AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -50,6 +52,9 @@ public abstract class AbstractMappingJacksonResponseBodyAdvice implements Respon
|
||||
if (body == null) {
|
||||
return null;
|
||||
}
|
||||
if (AbstractJacksonHttpMessageConverter.class.isAssignableFrom(converterType)) {
|
||||
return body;
|
||||
}
|
||||
MappingJacksonValue container = getOrCreateContainer(body);
|
||||
beforeBodyWriteInternal(container, contentType, returnType, request, response);
|
||||
return container;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -175,7 +175,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
ResolvableType targetResolvableType = null;
|
||||
message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
|
||||
for (HttpMessageConverter<?> converter : this.messageConverters) {
|
||||
Class<HttpMessageConverter<?>> converterClass = (Class<HttpMessageConverter<?>>) converter.getClass();
|
||||
Class<? extends HttpMessageConverter<?>> converterClass = (Class<? extends HttpMessageConverter<?>>) converter.getClass();
|
||||
ConverterType converterTypeToUse = null;
|
||||
if (converter instanceof GenericHttpMessageConverter<?> genericConverter) {
|
||||
if (genericConverter.canRead(targetType, contextClass, contentType)) {
|
||||
@@ -195,17 +195,17 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
}
|
||||
if (converterTypeToUse != null) {
|
||||
if (message.hasBody()) {
|
||||
HttpInputMessage msgToUse =
|
||||
getAdvice().beforeBodyRead(message, parameter, targetType, converterClass);
|
||||
HttpInputMessage msgToUse = this.advice.beforeBodyRead(message, parameter, targetType, converterClass);
|
||||
body = switch (converterTypeToUse) {
|
||||
case BASE -> ((HttpMessageConverter<T>) converter).read(targetClass, msgToUse);
|
||||
case GENERIC -> ((GenericHttpMessageConverter<?>) converter).read(targetType, contextClass, msgToUse);
|
||||
case SMART -> ((SmartHttpMessageConverter<?>) converter).read(targetResolvableType, msgToUse, null);
|
||||
case SMART -> ((SmartHttpMessageConverter<?>) converter).read(targetResolvableType, msgToUse,
|
||||
this.advice.determineReadHints(parameter, targetType, (Class<SmartHttpMessageConverter<?>>) converterClass));
|
||||
};
|
||||
body = getAdvice().afterBodyRead(body, msgToUse, parameter, targetType, converterClass);
|
||||
body = this.advice.afterBodyRead(body, msgToUse, parameter, targetType, converterClass);
|
||||
}
|
||||
else {
|
||||
body = getAdvice().handleEmptyBody(null, message, parameter, targetType, converterClass);
|
||||
body = this.advice.handleEmptyBody(null, message, parameter, targetType, converterClass);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -213,7 +213,7 @@ public abstract class AbstractMessageConverterMethodArgumentResolver implements
|
||||
}
|
||||
|
||||
if (body == NO_VALUE && noContentType && !message.hasBody()) {
|
||||
body = getAdvice().handleEmptyBody(
|
||||
body = this.advice.handleEmptyBody(
|
||||
null, message, parameter, targetType, NoContentTypeHttpMessageConverter.class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -324,7 +324,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||
}
|
||||
}
|
||||
else if (converter instanceof SmartHttpMessageConverter smartConverter) {
|
||||
targetResolvableType = getNestedTypeIfNeeded(ResolvableType.forMethodParameter(returnType));
|
||||
targetResolvableType = getNestedTypeIfNeeded(ResolvableType.forType(targetType));
|
||||
if (smartConverter.canWrite(targetResolvableType, valueType, selectedMediaType)) {
|
||||
converterTypeToUse = ConverterType.SMART;
|
||||
}
|
||||
@@ -343,7 +343,9 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
|
||||
switch (converterTypeToUse) {
|
||||
case BASE -> converter.write(body, selectedMediaType, outputMessage);
|
||||
case GENERIC -> ((GenericHttpMessageConverter) converter).write(body, targetType, selectedMediaType, outputMessage);
|
||||
case SMART -> ((SmartHttpMessageConverter) converter).write(body, targetResolvableType, selectedMediaType, outputMessage, null);
|
||||
case SMART -> ((SmartHttpMessageConverter) converter).write(body, targetResolvableType,
|
||||
selectedMediaType, outputMessage, getAdvice().determineWriteHints(body, returnType,
|
||||
selectedMediaType, (Class<? extends HttpMessageConverter<?>>) converter.getClass()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -18,12 +18,17 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.AbstractJacksonHttpMessageConverter;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
|
||||
import org.springframework.http.converter.json.MappingJacksonInputMessage;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -52,14 +57,28 @@ public class JsonViewRequestBodyAdvice extends RequestBodyAdviceAdapter {
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType,
|
||||
Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
|
||||
return (AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType) &&
|
||||
methodParameter.getParameterAnnotation(JsonView.class) != null);
|
||||
return methodParameter.getParameterAnnotation(JsonView.class) != null &&
|
||||
(AbstractJacksonHttpMessageConverter.class.isAssignableFrom(converterType) ||
|
||||
AbstractJackson2HttpMessageConverter.class.isAssignableFrom(converterType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter methodParameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> selectedConverterType) throws IOException {
|
||||
|
||||
if (AbstractJacksonHttpMessageConverter.class.isAssignableFrom(selectedConverterType)) {
|
||||
return inputMessage;
|
||||
}
|
||||
|
||||
return new MappingJacksonInputMessage(inputMessage.getBody(), inputMessage.getHeaders(), getJsonView(methodParameter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineReadHints(MethodParameter parameter, Type targetType, Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
return Collections.singletonMap(JsonView.class.getName(), getJsonView(parameter));
|
||||
}
|
||||
|
||||
private static Class<?> getJsonView(MethodParameter methodParameter) {
|
||||
JsonView ann = methodParameter.getParameterAnnotation(JsonView.class);
|
||||
Assert.state(ann != null, "No JsonView annotation");
|
||||
|
||||
@@ -68,8 +87,6 @@ public class JsonViewRequestBodyAdvice extends RequestBodyAdviceAdapter {
|
||||
throw new IllegalArgumentException(
|
||||
"@JsonView only supported for request body advice with exactly 1 class argument: " + methodParameter);
|
||||
}
|
||||
|
||||
return new MappingJacksonInputMessage(inputMessage.getBody(), inputMessage.getHeaders(), classes[0]);
|
||||
return classes[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2017 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonView;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -55,6 +59,15 @@ public class JsonViewResponseBodyAdvice extends AbstractMappingJacksonResponseBo
|
||||
protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType,
|
||||
MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
|
||||
bodyContainer.setSerializationView(getJsonView(returnType));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineWriteHints(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
|
||||
return Collections.singletonMap(JsonView.class.getName(), getJsonView(returnType));
|
||||
}
|
||||
|
||||
private static Class<?> getJsonView(MethodParameter returnType) {
|
||||
JsonView ann = returnType.getMethodAnnotation(JsonView.class);
|
||||
Assert.state(ann != null, "No JsonView annotation");
|
||||
|
||||
@@ -63,8 +76,6 @@ public class JsonViewResponseBodyAdvice extends AbstractMappingJacksonResponseBo
|
||||
throw new IllegalArgumentException(
|
||||
"@JsonView only supported for response body advice with exactly 1 class argument: " + returnType);
|
||||
}
|
||||
|
||||
bodyContainer.setSerializationView(classes[0]);
|
||||
return classes[0];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -18,12 +18,14 @@ package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
|
||||
/**
|
||||
* Allows customizing the request before its body is read and converted into an
|
||||
@@ -36,6 +38,7 @@ import org.springframework.http.converter.HttpMessageConverter;
|
||||
* {@code @ControllerAdvice} in which case they are auto-detected.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.2
|
||||
*/
|
||||
public interface RequestBodyAdvice {
|
||||
@@ -63,6 +66,20 @@ public interface RequestBodyAdvice {
|
||||
HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException;
|
||||
|
||||
/**
|
||||
* Invoked to determine read hints if the converter is a {@link SmartHttpMessageConverter}.
|
||||
* @param parameter the target method parameter
|
||||
* @param targetType the target type, not necessarily the same as the method
|
||||
* parameter type, for example, for {@code HttpEntity<String>}.
|
||||
* @param converterType the selected converter type
|
||||
* @return the hints determined otherwise {@code null}
|
||||
* @since 7.0
|
||||
*/
|
||||
default @Nullable Map<String, Object> determineReadHints(MethodParameter parameter,
|
||||
Type targetType, Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked third (and last) after the request body is converted to an Object.
|
||||
* @param body set to the converter Object before the first advice is called
|
||||
@@ -90,5 +107,4 @@ public interface RequestBodyAdvice {
|
||||
@Nullable Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter,
|
||||
Type targetType, Class<? extends HttpMessageConverter<?>> converterType);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2021 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -20,7 +20,9 @@ import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
@@ -28,6 +30,7 @@ import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
@@ -36,7 +39,7 @@ import org.springframework.web.method.ControllerAdviceBean;
|
||||
/**
|
||||
* Invokes {@link RequestBodyAdvice} and {@link ResponseBodyAdvice} where each
|
||||
* instance may be (and is most likely) wrapped with
|
||||
* {@link org.springframework.web.method.ControllerAdviceBean ControllerAdviceBean}.
|
||||
* {@link ControllerAdviceBean ControllerAdviceBean}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 4.2
|
||||
@@ -176,4 +179,38 @@ class RequestResponseBodyAdviceChain implements RequestBodyAdvice, ResponseBodyA
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineReadHints(MethodParameter parameter, Type targetType, Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
Map<String, Object> hints = null;
|
||||
for (RequestBodyAdvice advice : getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
|
||||
if (advice.supports(parameter, targetType, converterType)) {
|
||||
Map<String, Object> adviceHints = advice.determineReadHints(parameter, targetType, converterType);
|
||||
if (adviceHints != null) {
|
||||
if (hints == null) {
|
||||
hints = new HashMap<>(adviceHints.size());
|
||||
}
|
||||
hints.putAll(adviceHints);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public @Nullable Map<String, Object> determineWriteHints(@Nullable Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
|
||||
Map<String, Object> hints = null;
|
||||
for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
|
||||
if (advice.supports(returnType, selectedConverterType)) {
|
||||
Map<String, Object> adviceHints = ((ResponseBodyAdvice<Object>) advice).determineWriteHints(body, returnType, selectedContentType, selectedConverterType);
|
||||
if (adviceHints != null) {
|
||||
if (hints == null) {
|
||||
hints = new HashMap<>(adviceHints.size());
|
||||
}
|
||||
hints.putAll(adviceHints);
|
||||
}
|
||||
}
|
||||
}
|
||||
return hints;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2018 the original author or authors.
|
||||
* Copyright 2002-2025 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.
|
||||
@@ -16,11 +16,14 @@
|
||||
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
|
||||
@@ -35,6 +38,7 @@ import org.springframework.http.server.ServerHttpResponse;
|
||||
* will be auto-detected by both.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.1
|
||||
* @param <T> the body type
|
||||
*/
|
||||
@@ -65,4 +69,18 @@ public interface ResponseBodyAdvice<T> {
|
||||
Class<? extends HttpMessageConverter<?>> selectedConverterType,
|
||||
ServerHttpRequest request, ServerHttpResponse response);
|
||||
|
||||
/**
|
||||
* Invoked to determine write hints if the converter is a {@link SmartHttpMessageConverter}.
|
||||
* @param body the body to be written
|
||||
* @param returnType the return type of the controller method
|
||||
* @param selectedContentType the content type selected through content negotiation
|
||||
* @param selectedConverterType the converter type selected to write to the response
|
||||
* @return the hints determined otherwise {@code null}
|
||||
* @since 7.0
|
||||
*/
|
||||
default @Nullable Map<String, Object> determineWriteHints(@Nullable T body, MethodParameter returnType, MediaType selectedContentType,
|
||||
Class<? extends HttpMessageConverter<?>> selectedConverterType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -17,10 +17,14 @@
|
||||
package org.springframework.web.servlet.mvc.method.annotation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.context.support.StaticApplicationContext;
|
||||
@@ -29,7 +33,9 @@ import org.springframework.core.annotation.AnnotatedElementUtils;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.converter.HttpMessageConverter;
|
||||
import org.springframework.http.converter.SmartHttpMessageConverter;
|
||||
import org.springframework.http.converter.StringHttpMessageConverter;
|
||||
import org.springframework.http.converter.json.JacksonJsonHttpMessageConverter;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
@@ -52,6 +58,7 @@ import static org.mockito.Mockito.mock;
|
||||
* Tests for {@link RequestResponseBodyAdviceChain}.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @author Sebastien Deleuze
|
||||
* @since 4.2
|
||||
*/
|
||||
class RequestResponseBodyAdviceChainTests {
|
||||
@@ -131,6 +138,20 @@ class RequestResponseBodyAdviceChainTests {
|
||||
assertThat(actual).isEqualTo(this.body);
|
||||
}
|
||||
|
||||
@Test
|
||||
void controllerAdviceWithHints() {
|
||||
Object fooAdviceBean = createControllerAdviceBean(FooHintControllerAdvice.class);
|
||||
Object barAdviceBean = createControllerAdviceBean(BarHintControllerAdvice.class);
|
||||
RequestResponseBodyAdviceChain chain = new RequestResponseBodyAdviceChain(List.of(fooAdviceBean, barAdviceBean));
|
||||
|
||||
Map<String, Object> readHints = chain.determineReadHints(this.paramType, this.paramType.getGenericParameterType(),
|
||||
JacksonJsonHttpMessageConverter.class);
|
||||
assertThat(readHints).containsExactlyInAnyOrderEntriesOf(Map.of("foo", "String", "bar", "String"));
|
||||
|
||||
Map<String, Object> writeHints = chain.determineWriteHints(this.body, this.returnType, this.contentType, this.converterType);
|
||||
assertThat(writeHints).containsExactlyInAnyOrderEntriesOf(Map.of("foo", "body", "bar", "body"));
|
||||
}
|
||||
|
||||
private ControllerAdviceBean createControllerAdviceBean(Class<?> beanType) {
|
||||
StaticApplicationContext applicationContext = new StaticApplicationContext();
|
||||
applicationContext.registerSingleton(beanType.getSimpleName(), beanType);
|
||||
@@ -174,6 +195,64 @@ class RequestResponseBodyAdviceChainTests {
|
||||
}
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
private static class FooHintControllerAdvice extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<String> {
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String beforeBodyWrite(@Nullable String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineWriteHints(@Nullable String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
|
||||
return Collections.singletonMap("foo", Objects.requireNonNull(body));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineReadHints(MethodParameter parameter, Type targetType, Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
return Collections.singletonMap("foo", parameter.getParameterType().getSimpleName());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ControllerAdvice
|
||||
private static class BarHintControllerAdvice extends RequestBodyAdviceAdapter implements ResponseBodyAdvice<String> {
|
||||
@Override
|
||||
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String beforeBodyWrite(@Nullable String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineWriteHints(@Nullable String body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType) {
|
||||
return Collections.singletonMap("bar", Objects.requireNonNull(body));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Map<String, Object> determineReadHints(MethodParameter parameter, Type targetType, Class<? extends SmartHttpMessageConverter<?>> converterType) {
|
||||
return Collections.singletonMap("bar", parameter.getParameterType().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@ResponseBody
|
||||
|
||||
Reference in New Issue
Block a user