I am trying to handle exceptions using ExpressionEvaluatingRequestHandlerAdvice, have a transformer for a fail channel,
<int:transformer input-channel="afterFailureChannel" output-channel="validateOutputChannel" ref="testExceptionTransformer" method="handleLockServiceResponse"/>
In testExceptionTransformer, I am forming user defined exception and sending it in http response entity which I want to send as a rest api response, Even though transformer has outputChannel, application throws
org.springframework.messaging.core.DestinationResolutionException: no output-channel or replyChannel header available
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:452) ~[spring-integration-core-5.5.13.jar:5.5.13]
Could you please help?
Edit:
Transformer looks like this,
public ResponseEntity<Object> handleLockServiceResponse(Message<MessagingException> message) throws Exception {
ResponseEntity<Object> response = null;
LOGGER.error(message.getPayload().getFailedMessage().toString());
LOGGER.error(message.getPayload().getCause().toString());
try {
Throwable exception = message.getPayload().getCause();
if (exception.getCause() instanceof HttpClientErrorException) {
throw new handleValidationException(exception.getCause().getMessage());
}
}catch(handleValidationException ex){
return adapterErrorHandler.handleCustomValidationException(ex);
}
return response;
}
It indeed doesn't fail in your transformer since you have that output-channel
it fails in the initial gateway when it tries to correlate the reply message into a TemporaryReplyChannel
from headers. We need to see what your transformer does, but the rule of thumb is if you return a Message
from the transformer, you have to coyp headers from request message. However with an ExpressionEvaluatingRequestHandlerAdvice
and its failureChannel
it is a bit tricky.
The logic there is like this:
if (evalResult != null && this.failureChannel != null) {
MessagingException messagingException =
new MessageHandlingExpressionEvaluatingAdviceException(message, "Handler Failed",
unwrapThrowableIfNecessary(exception), evalResult);
ErrorMessage errorMessage = new ErrorMessage(messagingException);
this.messagingTemplate.send(this.failureChannel, errorMessage);
}
It becomes obvious that ErrorMessage
doesn't have a request message headers. So, you need to extract them from that exception via getFailedMessage()
and that's the one is sent to your service instrumented with that ExpressionEvaluatingRequestHandlerAdvice
.
We probably need to improve the doc on the matter: https://docs.spring.io/spring-integration/docs/current/reference/html/messaging-endpoints.html#expression-advice
UPDATE
So, now you return a ResponseEntity
from your transformer method and headers for the reply message is copied from that ErrorMessage
we send from the ExpressionEvaluatingRequestHandlerAdvice
. To preserve original message headers in the reply message you must do something like this:
public Message<ResponseEntity<Object>> handleLockServiceResponse(Message<MessagingException> message) throws Exception {
ResponseEntity<Object> response = null;
LOGGER.error(message.getPayload().getFailedMessage().toString());
LOGGER.error(message.getPayload().getCause().toString());
try {
Throwable exception = message.getPayload().getCause();
if (exception.getCause() instanceof HttpClientErrorException) {
throw new handleValidationException(exception.getCause().getMessage());
}
}catch(handleValidationException ex){
response = adapterErrorHandler.handleCustomValidationException(ex);
}
return MessageBuilder.withPayload(response).copyHeaders(message.getPayload().getFailedMessage().getHeaders()).build();
}