INT-4479: RequestEntity as root for status code

JIRA: https://jira.spring.io/browse/INT-4479

For better user experience expose a `RequestEntity<?>` as a root object
for evaluation context for `statusCodeExpression` execution
This commit is contained in:
Artem Bilan
2018-10-04 16:26:01 -04:00
committed by Gary Russell
parent a05004ae38
commit 262e624ef4
10 changed files with 130 additions and 76 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2017 the original author or authors.
* Copyright 2017-2018 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.
@@ -27,6 +27,7 @@ import org.springframework.expression.Expression;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.RequestEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.integration.dsl.ComponentsRegistration;
import org.springframework.integration.dsl.MessagingGatewaySpec;
@@ -277,7 +278,7 @@ public abstract class HttpInboundEndpointSupportSpec<S extends HttpInboundEndpoi
* @return the current Spec.
* @see HttpRequestHandlingEndpointSupport#setStatusCodeExpression(Expression)
*/
public S statusCodeFunction(Function<Void, ?> statusCodeFunction) {
public S statusCodeFunction(Function<RequestEntity<?>, ?> statusCodeFunction) {
return statusCodeExpression(new FunctionExpression<>(statusCodeFunction));
}

View File

@@ -25,6 +25,7 @@ import org.springframework.core.ResolvableType;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpRequest;
@@ -276,8 +277,8 @@ public class BaseHttpInboundEndpoint extends MessagingGatewaySupport implements
}
}
protected HttpStatus evaluateHttpStatus() {
Object value = this.statusCodeExpression.getValue(this.evaluationContext);
protected HttpStatus evaluateHttpStatus(HttpEntity<?> httpEntity) {
Object value = this.statusCodeExpression.getValue(this.evaluationContext, httpEntity);
return buildHttpStatus(value);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -27,6 +27,8 @@ import org.springframework.context.MessageSource;
import org.springframework.expression.Expression;
import org.springframework.expression.common.LiteralExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.RequestEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.messaging.Message;
import org.springframework.util.Assert;
@@ -52,15 +54,25 @@ import org.springframework.web.servlet.mvc.Controller;
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*
* @since 2.0
*/
public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSupport implements Controller {
private static final String DEFAULT_ERROR_CODE = "spring.integration.http.handler.error";
/**
* The View model key for the error code.
*/
public static final String DEFAULT_ERROR_CODE = "spring.integration.http.handler.error";
private static final String DEFAULT_REPLY_KEY = "reply";
/**
* The View model key for reply.
*/
public static final String DEFAULT_REPLY_KEY = "reply";
private static final String DEFAULT_ERRORS_KEY = "errors";
/**
* The View model key for errors.
*/
public static final String DEFAULT_ERRORS_KEY = "errors";
private volatile Expression viewExpression;
@@ -82,7 +94,6 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
/**
* Specify the view name.
*
* @param viewName The view name.
*/
public void setViewName(String viewName) {
@@ -94,7 +105,6 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
* Specify the key to be used when adding the reply Message or payload to the core map (will be payload only unless
* the value of {@link HttpRequestHandlingController#setExtractReplyPayload(boolean)} is <code>false</code>). The
* default key is "reply".
*
* @param replyKey The reply key.
*/
public void setReplyKey(String replyKey) {
@@ -104,7 +114,6 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
/**
* The key used to expose {@link Errors} in the core, in the case that message handling fails. Defaults to
* "errors".
*
* @param errorsKey The key value to set.
*/
public void setErrorsKey(String errorsKey) {
@@ -116,7 +125,6 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
* provided in an object error to be optionally translated in the standard MVC way using a {@link MessageSource}.
* The default value is <code>spring.integration.http.handler.error</code>. Three arguments are provided: the
* exception, its message and its stack trace as a String.
*
* @param errorCode The error code to set.
*/
public void setErrorCode(String errorCode) {
@@ -126,7 +134,6 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
/**
* Specifies a SpEL expression to evaluate in order to generate the view name.
* The EvaluationContext will be populated with the reply message as the root object,
*
* @param viewExpression The view expression.
*/
public void setViewExpression(Expression viewExpression) {
@@ -144,11 +151,14 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
* 'expectReply' property is true, it will also generate a response from the reply Message once received.
*/
@Override
public final ModelAndView handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws Exception {
public final ModelAndView handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {
ModelAndView modelAndView = new ModelAndView();
try {
Message<?> replyMessage = super.doHandleRequest(servletRequest, servletResponse);
ServletServerHttpRequest request = prepareRequest(servletRequest);
RequestEntity<Object> httpEntity = prepareRequestEntity(request);
Message<?> replyMessage = doHandleRequest(servletRequest, httpEntity, servletResponse);
ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
if (replyMessage != null) {
Object reply = setupResponseAndConvertReply(response, replyMessage);
@@ -156,7 +166,7 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
modelAndView.addObject(this.replyKey, reply);
}
else {
setStatusCodeIfNeeded(response);
setStatusCodeIfNeeded(response, httpEntity);
}
if (this.viewExpression != null) {
@@ -183,8 +193,9 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
PrintWriter stackTrace = new PrintWriter(new StringWriter());
e.printStackTrace(stackTrace);
errors.reject(this.errorCode, new Object[] { e, e.getMessage(), stackTrace.toString() },
"A Spring Integration handler raised an exception while handling an HTTP request. The exception is of type "
+ e.getClass() + " and it has a message: (" + e.getMessage() + ")");
"A Spring Integration handler raised an exception while handling an HTTP request. " +
"The exception is of type " + e.getClass() + " and it has a message: (" +
e.getMessage() + ")");
modelAndView.addObject(this.errorsKey, errors);
}
return modelAndView;

View File

@@ -36,6 +36,7 @@ import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.ResourceHttpMessageConverter;
@@ -245,33 +246,26 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
* Handles the HTTP request by generating a Message and sending it to the request channel. If this gateway's
* 'expectReply' property is true, it will also generate a response from the reply Message once received.
* @param servletRequest The servlet request.
* @param httpEntity the request entity to use.
* @param servletResponse The servlet response.
* @return The response Message.
* @throws IOException Any IOException.
*/
protected final Message<?> doHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws IOException {
protected final Message<?> doHandleRequest(HttpServletRequest servletRequest, RequestEntity<?> httpEntity,
HttpServletResponse servletResponse) {
if (isRunning()) {
return actualDoHandleRequest(servletRequest, servletResponse);
return actualDoHandleRequest(servletRequest, httpEntity, servletResponse);
}
else {
return createServiceUnavailableResponse();
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private Message<?> actualDoHandleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws IOException {
@SuppressWarnings("unchecked")
private Message<?> actualDoHandleRequest(HttpServletRequest servletRequest, RequestEntity<?> httpEntity,
HttpServletResponse servletResponse) {
this.activeCount.incrementAndGet();
try {
ServletServerHttpRequest request = this.prepareRequest(servletRequest);
Object requestBody = null;
if (isReadable(request)) {
requestBody = extractRequestBody(request);
}
HttpEntity httpEntity = new HttpEntity(requestBody, request.getHeaders());
StandardEvaluationContext evaluationContext = this.createEvaluationContext();
evaluationContext.setRootObject(httpEntity);
@@ -284,7 +278,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
Cookie[] requestCookies = servletRequest.getCookies();
if (!ObjectUtils.isEmpty(requestCookies)) {
Map<String, Cookie> cookies = new HashMap<String, Cookie>(requestCookies.length);
Map<String, Cookie> cookies = new HashMap<>(requestCookies.length);
for (Cookie requestCookie : requestCookies) {
cookies.put(requestCookie.getName(), requestCookie);
}
@@ -312,7 +306,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
evaluationContext.setVariable("matrixVariables", matrixVariables);
}
Map<String, Object> headers = getHeaderMapper().toHeaders(request.getHeaders());
Map<String, Object> headers = getHeaderMapper().toHeaders(httpEntity.getHeaders());
Object payload = null;
if (getPayloadExpression() != null) {
// create payload based on SpEL
@@ -330,8 +324,8 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
}
if (payload == null) {
if (requestBody != null) {
payload = requestBody;
if (httpEntity.getBody() != null) {
payload = httpEntity.getBody();
}
else {
payload = requestParams;
@@ -350,9 +344,9 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
Message<?> message = messageBuilder
.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_URL,
request.getURI().toString())
httpEntity.getUrl().toString())
.setHeader(org.springframework.integration.http.HttpHeaders.REQUEST_METHOD,
request.getMethod().toString())
httpEntity.getMethod().toString())
.setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL,
servletRequest.getUserPrincipal())
.build();
@@ -366,7 +360,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
if (getStatusCodeExpression() != null) {
reply = getMessageBuilderFactory().withPayload(e.getMessage())
.setHeader(org.springframework.integration.http.HttpHeaders.STATUS_CODE,
evaluateHttpStatus())
evaluateHttpStatus(httpEntity))
.build();
}
else {
@@ -419,9 +413,9 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
}
protected void setStatusCodeIfNeeded(ServerHttpResponse response) {
protected void setStatusCodeIfNeeded(ServerHttpResponse response, HttpEntity<?> httpEntity) {
if (getStatusCodeExpression() != null) {
HttpStatus httpStatus = evaluateHttpStatus();
HttpStatus httpStatus = evaluateHttpStatus(httpEntity);
if (httpStatus != null) {
response.setStatusCode(httpStatus);
}
@@ -437,7 +431,7 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
* @return the processed request (multipart wrapper if necessary)
* @see MultipartResolver#resolveMultipart
*/
private ServletServerHttpRequest prepareRequest(HttpServletRequest servletRequest) {
protected ServletServerHttpRequest prepareRequest(HttpServletRequest servletRequest) {
if (servletRequest instanceof MultipartHttpServletRequest) {
return new MultipartHttpInputMessage((MultipartHttpServletRequest) servletRequest);
}
@@ -472,8 +466,17 @@ public abstract class HttpRequestHandlingEndpointSupport extends BaseHttpInbound
return convertedMap;
}
protected RequestEntity<Object> prepareRequestEntity(ServletServerHttpRequest request) throws IOException {
Object requestBody = null;
if (isReadable(request)) {
requestBody = extractRequestBody(request);
}
return new RequestEntity<>(requestBody, request.getHeaders(), request.getMethod(), request.getURI());
}
@SuppressWarnings({ "unchecked", "rawtypes" })
private Object extractRequestBody(ServletServerHttpRequest request) throws IOException {
protected Object extractRequestBody(ServletServerHttpRequest request) throws IOException {
MediaType contentType = request.getHeaders().getContentType();
if (contentType == null) {
contentType = MediaType.APPLICATION_OCTET_STREAM;

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 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,13 +20,13 @@ import java.io.IOException;
import java.util.Collections;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServletServerHttpRequest;
@@ -60,6 +60,7 @@ import org.springframework.web.HttpRequestHandler;
* @author Mark Fisher
* @author Oleg Zhurakousky
* @author Artem Bilan
*
* @since 2.0
*/
public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport
@@ -81,7 +82,6 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
* Flag to determine if conversion and writing out of message handling exceptions should be attempted (default
* false, in which case they will simply be re-thrown). If the flag is true and no message converter can convert the
* exception a new exception will be thrown.
*
* @param convertExceptions the flag to set
*/
public void setConvertExceptions(boolean convertExceptions) {
@@ -94,15 +94,18 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
* response will be written by the {@link HttpMessageConverter}s.
*/
public final void handleRequest(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws ServletException, IOException {
throws IOException {
Object responseContent = null;
Message<?> responseMessage;
final ServletServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
final ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
ServletServerHttpRequest request = new ServletServerHttpRequest(servletRequest);
ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
RequestEntity<Object> httpEntity = prepareRequestEntity(request);
try {
responseMessage = super.doHandleRequest(servletRequest, servletResponse);
responseMessage = doHandleRequest(servletRequest, httpEntity, servletResponse);
if (responseMessage != null) {
responseContent = setupResponseAndConvertReply(response, responseMessage);
}
@@ -140,7 +143,7 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
}
}
else {
setStatusCodeIfNeeded(response);
setStatusCodeIfNeeded(response, httpEntity);
}
}
@@ -164,10 +167,11 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
@SuppressWarnings({ "unchecked", "rawtypes" })
private void writeResponse(Object content, ServletServerHttpResponse response, List<MediaType> acceptTypes)
throws IOException {
if (CollectionUtils.isEmpty(acceptTypes)) {
acceptTypes = Collections.singletonList(MediaType.ALL);
}
for (HttpMessageConverter converter : this.getMessageConverters()) {
for (HttpMessageConverter converter : getMessageConverters()) {
for (MediaType acceptType : acceptTypes) {
if (converter.canWrite(content.getClass(), acceptType)) {
converter.write(content, acceptType, response);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2016 the original author or authors.
* Copyright 2002-2018 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.
@@ -21,6 +21,7 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import java.io.IOException;
import java.util.Map;
import org.junit.Test;
@@ -28,6 +29,8 @@ import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.http.RequestEntity;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.http.AbstractHttpInboundTests;
import org.springframework.messaging.Message;
@@ -47,11 +50,11 @@ import org.springframework.web.servlet.HandlerMapping;
*/
public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends AbstractHttpInboundTests {
private static ExpressionParser PARSER = new SpelExpressionParser();
private static final ExpressionParser PARSER = new SpelExpressionParser();
@Test
public void withoutExpression() throws Exception {
public void withoutExpression() throws IOException {
DirectChannel echoChannel = new DirectChannel();
echoChannel.subscribe(message -> {
MessageChannel replyChannel = (MessageChannel) message.getHeaders().getReplyChannel();
@@ -62,7 +65,8 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
request.setContentType("text/plain");
request.setParameter("foo", "bar");
request.setContent("hello".getBytes());
String body = "hello";
request.setContent(body.getBytes());
request.setRequestURI("/fname/bill/lname/clinton");
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
@@ -78,7 +82,9 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
MockHttpServletResponse response = new MockHttpServletResponse();
Object result = gateway.doHandleRequest(request, response);
RequestEntity<Object> httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request));
Object result = gateway.doHandleRequest(request, httpEntity, response);
assertThat(result, instanceOf(Message.class));
assertEquals("hello", ((Message<?>) result).getPayload());
@@ -97,7 +103,8 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
request.setMethod("POST");
request.setContentType("text/plain");
request.setParameter("foo", "bar");
request.setContent("hello".getBytes());
String body = "hello";
request.setContent(body.getBytes());
String requestURI = "/fname/bill/lname/clinton";
@@ -120,7 +127,9 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
gateway.afterPropertiesSet();
gateway.start();
Object result = gateway.doHandleRequest(request, response);
RequestEntity<Object> httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request));
Object result = gateway.doHandleRequest(request, httpEntity, response);
assertThat(result, instanceOf(Message.class));
assertEquals("bill", ((Message<?>) result).getPayload());
}
@@ -140,7 +149,8 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
request.setMethod("POST");
request.setContentType("text/plain");
request.setParameter("foo", "bar");
request.setContent("hello".getBytes());
String body = "hello";
request.setContent(body.getBytes());
String requestURI = "/fname/bill/lname/clinton";
@@ -163,9 +173,15 @@ public class HttpRequestHandlingMessagingGatewayWithPathMappingTests extends Abs
gateway.afterPropertiesSet();
gateway.start();
Object result = gateway.doHandleRequest(request, response);
RequestEntity<Object> httpEntity = prepareRequestEntity(body, new ServletServerHttpRequest(request));
Object result = gateway.doHandleRequest(request, httpEntity, response);
assertThat(result, instanceOf(Message.class));
assertEquals("bill", ((Map<String, Object>) ((Message<?>) result).getPayload()).get("f"));
}
private static RequestEntity<Object> prepareRequestEntity(Object body, ServletServerHttpRequest request) throws IOException {
return new RequestEntity<>(body, request.getHeaders(), request.getMethod(), request.getURI());
}
}

View File

@@ -35,11 +35,11 @@ import org.springframework.core.ReactiveAdapterRegistry;
import org.springframework.core.ResolvableType;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.http.codec.HttpMessageReader;
import org.springframework.http.codec.HttpMessageWriter;
@@ -63,6 +63,7 @@ import org.springframework.web.server.WebHandler;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.util.function.Tuple2;
/**
* A {@link MessagingGatewaySupport} implementation for Spring WebFlux
@@ -151,16 +152,18 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W
return extractRequestBody(exchange)
.doOnSubscribe(s -> this.activeCount.incrementAndGet())
.switchIfEmpty(Mono.just(exchange.getRequest().getQueryParams()))
.map(body -> new HttpEntity<>(body, exchange.getRequest().getHeaders()))
.map(body ->
new RequestEntity<>(body, exchange.getRequest().getHeaders(),
exchange.getRequest().getMethod(), exchange.getRequest().getURI()))
.flatMap(entity -> buildMessage(entity, exchange))
.flatMap(requestMessage -> {
.flatMap(requestTuple -> {
if (this.expectReply) {
return sendAndReceiveMessageReactive(requestMessage)
return sendAndReceiveMessageReactive(requestTuple.getT1())
.flatMap(replyMessage -> populateResponse(exchange, replyMessage));
}
else {
send(requestMessage);
return setStatusCode(exchange);
send(requestTuple.getT1());
return setStatusCode(exchange, requestTuple.getT2());
}
})
.doOnTerminate(this.activeCount::decrementAndGet);
@@ -235,7 +238,9 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W
}
@SuppressWarnings("unchecked")
private Mono<Message<?>> buildMessage(HttpEntity<?> httpEntity, ServerWebExchange exchange) {
private Mono<Tuple2<Message<Object>, RequestEntity<?>>> buildMessage(RequestEntity<?> httpEntity,
ServerWebExchange exchange) {
ServerHttpRequest request = exchange.getRequest();
HttpHeaders requestHeaders = request.getHeaders();
Map<String, Object> exchangeAttributes = exchange.getAttributes();
@@ -258,7 +263,8 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W
}
Map<String, MultiValueMap<String, String>> matrixVariables =
(Map<String, MultiValueMap<String, String>>) exchangeAttributes.get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE);
(Map<String, MultiValueMap<String, String>>) exchangeAttributes
.get(HandlerMapping.MATRIX_VARIABLES_ATTRIBUTE);
if (!CollectionUtils.isEmpty(matrixVariables)) {
evaluationContext.setVariable("matrixVariables", matrixVariables);
@@ -314,7 +320,8 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W
messageBuilder
.setHeader(org.springframework.integration.http.HttpHeaders.USER_PRINCIPAL, principal))
.defaultIfEmpty(messageBuilder)
.map(AbstractIntegrationMessageBuilder::build);
.map(AbstractIntegrationMessageBuilder::build)
.zipWith(Mono.just(httpEntity));
}
private Mono<Void> populateResponse(ServerWebExchange exchange, Message<?> replyMessage) {
@@ -481,10 +488,10 @@ public class WebFluxInboundEndpoint extends BaseHttpInboundEndpoint implements W
}
private Mono<Void> setStatusCode(ServerWebExchange exchange) {
private Mono<Void> setStatusCode(ServerWebExchange exchange, RequestEntity<?> requestEntity) {
ServerHttpResponse response = exchange.getResponse();
if (getStatusCodeExpression() != null) {
HttpStatus httpStatus = evaluateHttpStatus();
HttpStatus httpStatus = evaluateHttpStatus(requestEntity);
if (httpStatus != null) {
response.setStatusCode(httpStatus);
}

View File

@@ -355,7 +355,10 @@ public class WebFluxDslTests {
.from(WebFlux.inboundChannelAdapter("/reactivePost")
.requestMapping(m -> m.methods(HttpMethod.POST))
.requestPayloadType(ResolvableType.forClassWithGenerics(Flux.class, String.class))
.statusCodeFunction(m -> HttpStatus.ACCEPTED))
.statusCodeFunction(e ->
HttpMethod.POST.equals(e.getMethod())
? HttpStatus.ACCEPTED
: HttpStatus.BAD_REQUEST))
.channel(c -> c.queue("storeChannel"))
.get();
}

View File

@@ -360,10 +360,11 @@ instances of which can be injected into the `HttpRequestHandlingEndpointSupport`
Starting with version 4.1, you can configure the `<http:inbound-channel-adapter>` with a `status-code-expression` to override the default `200 OK` status.
The expression must return an object that can be converted to an `org.springframework.http.HttpStatus` enum value.
The `evaluationContext` has a `BeanResolver` but no variables, so the usage of this attribute is somewhat limited.
The `evaluationContext` has a `BeanResolver` and, starting with version 5.1, is supplied with the `RequestEntity<?>` as root object.
An example might be to resolve, at runtime, some scoped bean that returns a status code value.
However, most likely, it is set to a fixed value such as `status-code=expression="204"` (No Content), or `status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"`.
By default, `status-code-expression` is null, meaning that the normal '200 OK' response status is returned.
Using the `RequestEntity<?>` as root object, the status code can be conditional e.g. on the request method, some header, URI content or even request body.
The following example shows how to set the status code to `ACCEPTED`:
====

View File

@@ -165,3 +165,10 @@ When a `JmsMessageDrivenEndpoint` or `JmsInboundGateway` is stopped, the associa
You can configure the endpoints to revert to the previous behavior.
See <<jms>> for more information.
[[x51.-http]]
=== HTTP/WebFlux Support
The `statusCodeExpression` (and `Function`) is now supplied with the `RequestEntity<?>` as a root object for evaluation context, so request headers, method, URI and body are available for target status code calculation.
See <<http>> and <<webflux>> for more information.