Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GH-3283: HTTP Inbound handle SpEL errors #3289

Merged
merged 2 commits into from
May 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,10 @@
import org.springframework.integration.support.AbstractIntegrationMessageBuilder;
import org.springframework.integration.support.json.JacksonPresent;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHeaders;
import org.springframework.messaging.MessagingException;
import org.springframework.messaging.converter.MessageConversionException;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
Expand Down Expand Up @@ -267,30 +269,46 @@ private Message<?> actualDoHandleRequest(HttpServletRequest servletRequest, Requ

Map<String, Object> headers = getHeaderMapper().toHeaders(httpEntity.getHeaders());
Object payload = null;
if (getPayloadExpression() != null) {
// create payload based on SpEL
payload = getPayloadExpression().getValue(evaluationContext);
}
Message<?> message = null;
try {
if (getPayloadExpression() != null) {
// create payload based on SpEL
payload = getPayloadExpression().getValue(evaluationContext);
}

if (!CollectionUtils.isEmpty(getHeaderExpressions())) {
headers.putAll(
ExpressionEvalMap.from(getHeaderExpressions())
.usingEvaluationContext(evaluationContext)
.withRoot(httpEntity)
.build());
}
if (!CollectionUtils.isEmpty(getHeaderExpressions())) {
headers.putAll(
ExpressionEvalMap.from(getHeaderExpressions())
.usingEvaluationContext(evaluationContext)
.withRoot(httpEntity)
.build());
}

if (payload == null) {
if (httpEntity.getBody() != null) {
payload = httpEntity.getBody();
if (payload == null) {
if (httpEntity.getBody() != null) {
payload = httpEntity.getBody();
}
else {
payload = requestParams;
}
}

message = prepareRequestMessage(servletRequest, httpEntity, headers, payload);
}
catch (Exception ex) {
MessageConversionException conversionException =
new MessageConversionException("Cannot create request message", ex);
MessageChannel errorChannel = getErrorChannel();
if (errorChannel != null) {
this.messagingTemplate.send(errorChannel,
buildErrorMessage(null,
conversionException));
}
else {
payload = requestParams;
throw conversionException;
}
}

Message<?> message = prepareRequestMessage(servletRequest, httpEntity, headers, payload);

Message<?> reply = null;
if (isExpectReply()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.ClientHttpRequestFactory;
Expand All @@ -56,6 +57,7 @@
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.support.ErrorMessage;
import org.springframework.mock.web.MockPart;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.vote.AffirmativeBased;
Expand All @@ -81,6 +83,7 @@
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.server.ResponseStatusException;
import org.springframework.web.servlet.DispatcherServlet;

/**
Expand Down Expand Up @@ -235,6 +238,30 @@ public void testValidation() throws Exception {
flowRegistration.destroy();
}

@Test
public void testBadRequest() throws Exception {
IntegrationFlow flow =
IntegrationFlows.from(
Http.inboundGateway("/badRequest")
.errorChannel((message, timeout) -> {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
"Not valid request param", ((ErrorMessage) message).getPayload());
})
.payloadExpression("#requestParams.p1"))
.get();

IntegrationFlowContext.IntegrationFlowRegistration flowRegistration =
this.integrationFlowContext.registration(flow).register();

this.mockMvc.perform(
get("/badRequest")
.with(httpBasic("user", "user"))
.param("p2", "P2"))
.andExpect(status().isBadRequest())
.andExpect(status().reason("Not valid request param"));

flowRegistration.destroy();
}

@Configuration
@EnableWebSecurity
Expand Down
5 changes: 5 additions & 0 deletions src/reference/asciidoc/http.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,11 @@ If the error flow times out after a main flow timeout, `500 Internal Server Erro
NOTE: Previously, the default status code for a timeout was `200 OK`.
To restore that behavior, set `reply-timeout-status-code-expression="200"`.

Also starting with version 5.4, an error that is encountered while preparing the request message is sent to the error channel (if provided).
A decision about throwing an appropriate exception should be done in the error flow by examining the exception.
Previously, any exceptions were simply thrown, causing an HTTP 500 server error response status, but in some cases the problem can be caused by incorrect request params, so a `ResponseStatusException` with a 4xx client error status should be thrown instead.
See `ResponseStatusException` for more information.
The `ErrorMessage` sent to this error channel contains the original exception as the payload for analysis.
==== URI Template Variables and Expressions

By using the `path` attribute in conjunction with the `payload-expression` attribute and the `header` element, you have a high degree of flexibility for mapping inbound request data.
Expand Down