Reputation: 833
I am exposing a rest service using "CamelHttpTransportServlet" that receive orders and place in jms queue. The code works fine on happy path and returns 200 response. I have written Processor to validate the input JSON, and set http_response_code based on the input.
The issue is - for invalid requests though failure response code - 400 is set, the flow continues to the next route and pushes the data to the queue instead of sending the 400 response back to the calling app.
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
from("direct:checkInput")
.process(new Processor() {
@Override
public void process(final Exchange exchange) throws Exception {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Can someone advise what is missing here and provide a sample if possible?
Trying to implement onException approach:
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
onException(CustomException.class).handled(true)
.setHeader(Exchange.HTTP_RESPONSE_CODE, code)
.setBody(jsonObject);
from("direct:checkInput")
.process(new Processor() {
@Override
public void process(final Exchange exchange) throws Exception {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
throw CustomException(code, jsonObject)
}
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
However I could not figure out how to pass the parameters - code,jsonObject from processor to onException block.
Any help on this? Is this feasible?
Upvotes: 6
Views: 7760
Reputation:
Setting ROUTE_STOP property to true in the processor should prevent further flow and return your response:
...
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
exchange.setProperty(Exchange.ROUTE_STOP, Boolean.TRUE);
...
Upvotes: 1
Reputation: 12839
I'd use something along the lines of the code example below:
onException(CustomException.class)
.handled(true)
.bean(PrepareErrorResponse.class)
.log("Error response processed");
rest("/ordermanagement")
.post("/order")
.to("direct:checkInput");
from("direct:checkInput")
.process((Exchange exchange) -> {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == "" || requestBody== null) {
throw new CustomException(code, jsonObject);
}
})
.to("direct:sendToQ");
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Camel will store any exception caught in the exchange's property and should be therefore obtainable via the Exchange.EXCEPTION_CAUGHT
property key. The sample below illustrates how such a custom error message bean can look like:
public class PrepareErrorResponse {
@Handler
public void prepareErrorResponse(Exchange exchange) {
Throwable cause = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class);
if (cause instanceof CustomException) {
CustomException validationEx = (CustomException) cause;
// ...
}
Message msg = exchange.getOut();
msg.setHeader(Exchange.CONTENT_TYPE, MediaType.APPLICATION_JSON);
msg.setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
JsonObject errorMessage = new JsonObject();
errorMessage.put("error", "Bad Request");
errorMessage.put("reason", cause.getMessage());
msg.setBody(errorMessage.toString());
// we need to do the fault=false below in order to prevent a
// HTTP 500 error code from being returned
msg.setFault(false);
}
}
Camel provides a couple of ways actually to deal with exceptions. The presented way here is just one example. The proposed code however allows to use custom redelivery strategies for different caught exceptions as well as additional stuff. If the error could get resolved within the exception handler, the route is proceeded at the point the exception occurred (i.e. temporary network issue with a redelivery strategy applied). If the error could not get fixed within the handler, the exchange will be stopped. Usually one would then send the currently processed message to a DLQ and log something about the error.
Note that this example will assume that CustomException
is an unchecked exception as the processor is replaced with a simpler lambda. If you can't or don't want to use such an exception (or lambda expressions) replace the lambda-processor with new Processor() { @Override public void process(Exchange exchange) throws Exception { ... } }
construct.
Upvotes: 7
Reputation: 27048
Here is one way to do it. You can use choice
rest("/ordermanagement")
.post("/order").to("direct:checkInput");
from("direct:checkInput")
.process(exchange -> {
String requestBody = exchange.getIn().getBody(String.class);
if(requestBody == null || requestBody.equals("")) {
exchange.getIn().setBody("{ "error": Bad Request}");
exchange.getIn().setHeader(Exchange.CONTENT_TYPE, "application/json");
exchange.getIn().setHeader(Exchange.HTTP_RESPONSE_CODE, 400);
}
})
.choice()
.when(exchange -> {
Object header = exchange.getIn().getHeader(Exchange.HTTP_RESPONSE_CODE);
return header != null && header.equals(400);
})
.stop()
.otherwise()
.to("direct:sendToQ")
.endChoice();
from("direct:sendToQ")
.to("jms:queue:orderReceiver")
.log("Sent to JMS");
Upvotes: 6