Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Apache Camel REST DSL - Validating Request Payload and return error response

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?

like image 731
jack Avatar asked Feb 27 '18 14:02

jack


People also ask

How does Apache handle error in camel?

Using onException to handle known exceptions is a very powerful feature in Camel. You can mark the exception as being handled with the handle DSL, so the caller will not receive the caused exception as a response. The handle is a Predicate that is overloaded to accept three types of parameters: Boolean.

What is DSL in camel?

Camel uses a Java Domain Specific Language or DSL for creating Enterprise Integration Patterns or Routes in a variety of domain-specific languages (DSL) as listed below: Java DSL - A Java based DSL using the fluent builder style.


1 Answers

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.

like image 177
Roman Vottner Avatar answered Oct 09 '22 21:10

Roman Vottner