INT-3447: HTTP-inbound: status-code-expression
JIRA: https://jira.spring.io/browse/INT-3447 Polishing
This commit is contained in:
committed by
Gary Russell
parent
c309b031f8
commit
93be035cae
@@ -176,6 +176,12 @@ public class HttpInboundEndpointParser extends AbstractSingleBeanDefinitionParse
|
||||
builder.addPropertyValue("requestMapping", requestMappingDef);
|
||||
|
||||
IntegrationNamespaceUtils.setValueIfAttributeDefined(builder, element, "request-payload-type", "requestPayloadType");
|
||||
|
||||
BeanDefinition statusCodeExpressionDef =
|
||||
IntegrationNamespaceUtils.createExpressionDefIfAttributeDefined("status-code-expression", element);
|
||||
if (statusCodeExpressionDef != null) {
|
||||
builder.addPropertyValue("statusCodeExpression", statusCodeExpressionDef);
|
||||
}
|
||||
}
|
||||
|
||||
private String getInputChannelAttributeName() {
|
||||
|
||||
@@ -51,6 +51,7 @@ 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 {
|
||||
@@ -148,12 +149,16 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
|
||||
ModelAndView modelAndView = new ModelAndView();
|
||||
try {
|
||||
Message<?> replyMessage = super.doHandleRequest(servletRequest, servletResponse);
|
||||
ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
|
||||
if (replyMessage != null) {
|
||||
ServletServerHttpResponse response = new ServletServerHttpResponse(servletResponse);
|
||||
Object reply = setupResponseAndConvertReply(response, replyMessage);
|
||||
response.close();
|
||||
modelAndView.addObject(this.replyKey, reply);
|
||||
}
|
||||
else {
|
||||
setStatusCodeIfNeeded(response);
|
||||
}
|
||||
|
||||
if (this.viewExpression != null) {
|
||||
Object view;
|
||||
if (replyMessage != null) {
|
||||
@@ -184,4 +189,5 @@ public class HttpRequestHandlingController extends HttpRequestHandlingEndpointSu
|
||||
}
|
||||
return modelAndView;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import javax.xml.transform.Source;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.http.HttpEntity;
|
||||
@@ -151,6 +152,10 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
|
||||
private volatile boolean shuttingDown;
|
||||
|
||||
private volatile Expression statusCodeExpression;
|
||||
|
||||
private volatile EvaluationContext evaluationContext;
|
||||
|
||||
private final AtomicInteger activeCount = new AtomicInteger();
|
||||
|
||||
public HttpRequestHandlingEndpointSupport() {
|
||||
@@ -315,6 +320,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
this.multipartResolver = multipartResolver;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the {@link Expression} to resolve a status code for Response
|
||||
* to override the default '200 OK'.
|
||||
* <p> The {@link #statusCodeExpression} is applied only for the one-way {@code <http:inbound-channel-adapter/>}.
|
||||
* The {@code <http:inbound-gateway/>} resolves an {@link HttpStatus} from the
|
||||
* {@link org.springframework.integration.http.HttpHeaders#STATUS_CODE} reply {@link Message} header.
|
||||
* @param statusCodeExpression The status code Expression.
|
||||
* @since 4.1
|
||||
*/
|
||||
public void setStatusCodeExpression(Expression statusCodeExpression) {
|
||||
this.statusCodeExpression = statusCodeExpression;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getComponentType() {
|
||||
return (this.expectReply) ? "http:inbound-gateway" : "http:inbound-channel-adapter";
|
||||
@@ -351,6 +369,15 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
this.messageConverters.addAll(this.defaultMessageConverters);
|
||||
}
|
||||
this.validateSupportedMethods();
|
||||
|
||||
if (this.expectReply && this.statusCodeExpression != null) {
|
||||
logger.warn("The 'statusCodeExpression' is ignored when " +
|
||||
"this component is configured as request/reply gateway");
|
||||
}
|
||||
|
||||
if (this.statusCodeExpression != null) {
|
||||
this.evaluationContext = createEvaluationContext();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -515,6 +542,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
|
||||
}
|
||||
|
||||
protected void setStatusCodeIfNeeded(ServletServerHttpResponse response) {
|
||||
if (this.statusCodeExpression != null) {
|
||||
if (this.evaluationContext == null) {
|
||||
this.evaluationContext = createEvaluationContext();
|
||||
}
|
||||
Object value = this.statusCodeExpression.getValue(this.evaluationContext);
|
||||
HttpStatus httpStatus = buildHttpStatus(value);
|
||||
if (httpStatus != null) {
|
||||
response.setStatusCode(httpStatus);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares an instance of {@link ServletServerHttpRequest} from the raw
|
||||
* {@link HttpServletRequest}. Also converts the request into a multipart request to
|
||||
@@ -586,15 +626,19 @@ public abstract class HttpRequestHandlingEndpointSupport extends MessagingGatewa
|
||||
|
||||
private HttpStatus resolveHttpStatusFromHeaders(MessageHeaders headers) {
|
||||
Object httpStatusFromHeader = headers.get(org.springframework.integration.http.HttpHeaders.STATUS_CODE);
|
||||
return buildHttpStatus(httpStatusFromHeader);
|
||||
}
|
||||
|
||||
private HttpStatus buildHttpStatus(Object httpStatusValue) {
|
||||
HttpStatus httpStatus = null;
|
||||
if (httpStatusFromHeader instanceof HttpStatus) {
|
||||
httpStatus = (HttpStatus) httpStatusFromHeader;
|
||||
if (httpStatusValue instanceof HttpStatus) {
|
||||
httpStatus = (HttpStatus) httpStatusValue;
|
||||
}
|
||||
else if (httpStatusFromHeader instanceof Integer) {
|
||||
httpStatus = HttpStatus.valueOf((Integer) httpStatusFromHeader);
|
||||
else if (httpStatusValue instanceof Integer) {
|
||||
httpStatus = HttpStatus.valueOf((Integer) httpStatusValue);
|
||||
}
|
||||
else if (httpStatusFromHeader instanceof String) {
|
||||
httpStatus = HttpStatus.valueOf(Integer.parseInt((String) httpStatusFromHeader));
|
||||
else if (httpStatusValue instanceof String) {
|
||||
httpStatus = HttpStatus.valueOf(Integer.parseInt((String) httpStatusValue));
|
||||
}
|
||||
return httpStatus;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -57,9 +57,11 @@ import org.springframework.web.HttpRequestHandler;
|
||||
*
|
||||
* @author Mark Fisher
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport implements HttpRequestHandler {
|
||||
public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndpointSupport
|
||||
implements HttpRequestHandler {
|
||||
|
||||
private volatile boolean convertExceptions;
|
||||
|
||||
@@ -115,6 +117,9 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
|
||||
this.writeResponse(responseContent, response, request.getHeaders().getAccept());
|
||||
}
|
||||
}
|
||||
else {
|
||||
setStatusCodeIfNeeded(response);
|
||||
}
|
||||
}
|
||||
|
||||
private Object handleExceptionInternal(Exception e) throws IOException {
|
||||
@@ -135,7 +140,8 @@ public class HttpRequestHandlingMessagingGateway extends HttpRequestHandlingEndp
|
||||
}
|
||||
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
private void writeResponse(Object content, ServletServerHttpResponse response, List<MediaType> acceptTypes) throws IOException {
|
||||
private void writeResponse(Object content, ServletServerHttpResponse response, List<MediaType> acceptTypes)
|
||||
throws IOException {
|
||||
if (CollectionUtils.isEmpty(acceptTypes)) {
|
||||
acceptTypes = Collections.singletonList(MediaType.ALL);
|
||||
}
|
||||
|
||||
@@ -52,6 +52,23 @@
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="status-code-expression" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation>
|
||||
A SpEL expression that resolves to an 'HttpStatus' code when rendering a response.
|
||||
The expression must return the object which can be converted to a
|
||||
'org.springframework.http.HttpStatus' enum value.
|
||||
The 'evaluationContext' has a 'BeanResolver' but no variables, so the usage of this attribute
|
||||
is somewhat limited.
|
||||
An example might be to resolve, at runtime, some scoped Bean that returns an
|
||||
'HttpStatus' value.
|
||||
By default 'status-code-expression' is null, meaning that the default '200 OK' response status
|
||||
will be returned.
|
||||
The 'http:inbound-gateway' resolves the 'status code' from the 'http_statusCode' header of the reply
|
||||
Message.
|
||||
</xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attributeGroup ref="inboundCommonAttributes" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
@@ -211,7 +228,7 @@
|
||||
The expression can resolve to a view name or View object.
|
||||
In the case of 'inbound-gateway' the root object of the evaluation context is the reply message.
|
||||
In the case of 'inbound-channel-adapter' the 'evaluationContext' for this expression
|
||||
is rather lightweight, because there is no reply message, ; it has a
|
||||
is rather lightweight, because there is no reply message, it has a
|
||||
'BeanResolver' but no variables,
|
||||
so the usage of this attribute is somewhat limited.
|
||||
An example might be to resolve, at runtime, some scoped Bean that returns a
|
||||
|
||||
@@ -20,7 +20,8 @@
|
||||
<si:queue capacity="1"/>
|
||||
</si:channel>
|
||||
|
||||
<inbound-channel-adapter id="defaultAdapter" channel="requests" error-channel="errorChannel"/>
|
||||
<inbound-channel-adapter id="defaultAdapter" channel="requests" error-channel="errorChannel"
|
||||
status-code-expression="'101'"/>
|
||||
|
||||
<inbound-channel-adapter id="postOnlyAdapter" path="/postOnly" channel="requests" supported-methods="POST"/>
|
||||
|
||||
@@ -38,7 +39,8 @@
|
||||
|
||||
<inbound-channel-adapter id="putOrDeleteAdapter" channel="requests" supported-methods="PUT, delete"/>
|
||||
|
||||
<inbound-channel-adapter id="inboundController" channel="requests" view-name="foo" error-code="oops">
|
||||
<inbound-channel-adapter id="inboundController" channel="requests" view-name="foo" error-code="oops"
|
||||
status-code-expression="T(org.springframework.http.HttpStatus).ACCEPTED">
|
||||
<request-mapping headers="BAR"/>
|
||||
</inbound-channel-adapter>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -128,7 +128,7 @@ public class HttpInboundChannelAdapterParserTests extends AbstractHttpInboundTes
|
||||
request.setParameter("foo", "bar");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
defaultAdapter.handleRequest(request, response);
|
||||
assertEquals(HttpServletResponse.SC_OK, response.getStatus());
|
||||
assertEquals(HttpServletResponse.SC_SWITCHING_PROTOCOLS, response.getStatus());
|
||||
Message<?> message = requests.receive(0);
|
||||
assertNotNull(message);
|
||||
Object payload = message.getPayload();
|
||||
@@ -256,6 +256,15 @@ public class HttpInboundChannelAdapterParserTests extends AbstractHttpInboundTes
|
||||
assertEquals("oops", errorCode);
|
||||
Expression viewExpression = TestUtils.getPropertyValue(inboundController, "viewExpression", Expression.class);
|
||||
assertEquals("foo", viewExpression.getExpressionString());
|
||||
|
||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||
request.setMethod("GET");
|
||||
request.setParameter("foo", "bar");
|
||||
MockHttpServletResponse response = new MockHttpServletResponse();
|
||||
inboundController.handleRequest(request, response);
|
||||
assertEquals(HttpServletResponse.SC_ACCEPTED, response.getStatus());
|
||||
Message<?> message = requests.receive(0);
|
||||
assertNotNull(message);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2013 the original author or authors.
|
||||
* Copyright 2002-2014 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.
|
||||
@@ -31,6 +31,7 @@ import java.util.List;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.expression.common.LiteralExpression;
|
||||
import org.springframework.http.HttpInputMessage;
|
||||
import org.springframework.http.HttpOutputMessage;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -116,6 +117,7 @@ public class HttpRequestHandlingMessagingGatewayTests extends AbstractHttpInboun
|
||||
}
|
||||
});
|
||||
HttpRequestHandlingMessagingGateway gateway = new HttpRequestHandlingMessagingGateway(true);
|
||||
gateway.setStatusCodeExpression(new LiteralExpression("foo"));
|
||||
gateway.setBeanFactory(mock(BeanFactory.class));
|
||||
gateway.setRequestPayloadType(String.class);
|
||||
gateway.setRequestChannel(requestChannel);
|
||||
|
||||
@@ -317,6 +317,29 @@ By default the HTTP request will be generated using an instance of <classname>Si
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
<para><emphasis>Response StatusCode</emphasis></para>
|
||||
<para>
|
||||
Starting with <emphasis>version 4.1</emphasis> the <code><http:inbound-channel-adapter></code>
|
||||
can be configured with a <code>status-code-expression</code> to override the default <code>200 OK</code> status.
|
||||
The expression must return an object which can be converted to a
|
||||
<classname>org.springframework.http.HttpStatus</classname> enum value.
|
||||
The <code>evaluationContext</code> has a <classname>BeanResolver</classname> but no variables,
|
||||
so the usage of this attribute is somewhat limited.
|
||||
An example might be to resolve, at runtime, some scoped Bean that returns a status code value but,
|
||||
most likely, it will be set to a fixed value
|
||||
such as <code>status-code=expression="'204'"</code> (No Content), or
|
||||
<code>status-code-expression="T(org.springframework.http.HttpStatus).NO_CONTENT"</code>.
|
||||
By default, <code>status-code-expression</code> is null meaning that the normal '200 OK' response status
|
||||
will be returned.
|
||||
<programlisting language="xml"><![CDATA[<http:inbound-channel-adapter id="inboundController"
|
||||
channel="requests" view-name="foo" error-code="oops"
|
||||
status-code-expression="T(org.springframework.http.HttpStatus).ACCEPTED">
|
||||
<request-mapping headers="BAR"/>
|
||||
</http:inbound-channel-adapter>]]></programlisting>
|
||||
The <code><http:inbound-gateway></code> resolves the 'status code' from the <code>http_statusCode</code>
|
||||
header of the reply Message.
|
||||
</para>
|
||||
|
||||
<para><emphasis>URI Template Variables and Expressions</emphasis></para>
|
||||
<para>
|
||||
By Using the <emphasis>path</emphasis> attribute in conjunction with the
|
||||
|
||||
@@ -46,5 +46,13 @@
|
||||
before sending the request.
|
||||
</para>
|
||||
</section>
|
||||
<section id="4.1-http-status-code">
|
||||
<title>Http Inbound Channel Adapter and StatusCode</title>
|
||||
<para>
|
||||
The <code><http:inbound-channel-adapter></code> can now be configured with a
|
||||
<code>status-code-expression</code> to override the default <code>200 OK</code> status.
|
||||
See <xref linkend="http-namespace"/> for more information.
|
||||
</para>
|
||||
</section>
|
||||
</section>
|
||||
</chapter>
|
||||
|
||||
Reference in New Issue
Block a user