简体   繁体   中英

How to handle exceptions from jackson binding with Camel REST DSL?

I am using Camel 2.14.1 to build some RESTful services which use the Jackson json binding features of the Camel REST DSL. This is working great, but I'd like to be able to send a custom response to a client when something goes wrong during the binding.

For instance, if a NumberFormatException is thrown when converting an input string value to an integer, because a client mistakenly sent characters which were not numbers. This exception is not a Jackson specific exception, so I cannot catch it to reply to a client with a binding failure message, as it could be a legitimate internal error.

Similarly, I'd like to catch any other exceptions thrown during the inbound binding of json to a POJO, and handle those in a similar way, at the very least informing the client that their payload was invalid.

Since this is all handled "above" my code, I can't see how to easily handle such errors, as it's all dealt with in a library I have no control over. I am using Camel's onException clause to handle my own exceptions, so I'm aware of that, just not sure how exactly to achieve what I need to...

Worst case, I guess I could take every client input as a string and bind it myself, but that seems to rather defeat the point of Jackson.

tl;dr: Is there a pattern in Camel which would allow me to return a custom response when POJO binding fails from Jackson, using the REST DSL?

Edit - Here's some psuedocode to illustrate what I'm trying to do:

class errorHandler {

  void handleException(Exchange exchange, @ExchangeException Exception exception) {

    if (myException.class.isAssignableFrom(exception.getClass())) {
      // Set exchange body and response code header
      // This works as expected, though could probably do this more nicely
    }

    else if (camelBindingOrJacksonBindingException) {
      // Some other custom handling like above, but specific for these exception types
    }

    else {
      // Generic catch all with generic error response body
      // This also works fine
    }
  }
}

Currently I end up catching binding/Jackson exceptions in the final else block, and that's what I'm trying to avoid as really it would be more useful for the client to know their input wasn't valid. This seems equivalent to isaac.hazan's answer below.

The issue in doing this is that I can't find whether Jackson binding exceptions have a parent class which I can catch, a list of all such possible exceptions, or a way of telling that it came from binding.

The second issue is that I cannot tell whether camel binding exceptions such as NumberFormatException when Camel expects an int from a binding annotation on a method like: @Header int number , are genuinely from Camel, or from my own code. Very reluctant to catch any old NumberFormatException because if further internal changes could throw this, I'd end up returning an incorrect response. Is there maybe some way to figure out where the exception has come from (whether it was at binding time)?

I had a similar issue in one of my applications. I solved it by having a dedicated processor with a method for each type of exception.

For example you could have the following 2 handlers, one as a specific exception and another one for any other exception:

    <onException id="handleAmazonServiceException">
        <exception>com.amazonaws.AmazonServiceException</exception>
        <handled><constant>true</constant></handled>
        <bean ref="errorHandlerProcessor" method="handleAmazonServiceException" />
        <setHeader headerName="CamelFileName">
            <simple>{{local.failed.records.dir}}/${header.S3Prefix}/${date:now:yyyy-MM-dd}/${header.failedRecordFileName}</simple>
        </setHeader>
        <to id="failedRecordsFile" uri="ref:s3FailedRecordsEndpoint" />
    </onException>

    <onException id="handleGeneralException">
        <exception>org.apache.camel.CamelExecutionException</exception>
        <exception>java.lang.Exception</exception>
        <handled><constant>true</constant></handled>
        <bean ref="errorHandlerProcessor" method="handleGeneralException" />
        <setHeader headerName="CamelFileName">
            <simple>{{local.failed.records.dir}}/${header.S3Prefix}/${date:now:yyyy-MM-dd}/${header.failedRecordFileName}</simple>
        </setHeader>
        <to id="failedRecordsFile" uri="ref:s3FailedRecordsEndpoint" />
    </onException>

The key in the above is the "errorHandlerProcessor" bean which is in charge of handling any kind of exception. It is a simple way of handling all of you errors from a single place.

It looks like this:

@Service(value = "errorHandlerProcessor")
public class ErrorHandlerProcessor
{
// General handler
public void handleGeneralException(Exchange exchange)
{
    Throwable caused = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Throwable.class);
    logger.error("An unexpected error camel error occurred:" + caused.getMessage());
    logger.debug("Caught exception within the camel engine:", caused);
    ...
}

//AmazonClientException handler
public void handleAmazonClientException(Exchange exchange)
{
    AmazonClientException caused = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, AmazonClientException.class);
    StringBuffer errorMessage = new StringBuffer();
    errorMessage.append("AmazonClientException,AWS communication problem, Error Message: " + caused.getMessage());
    ...
}
}

In the end I ended up using the Camel onException clause for anything I could find in Jackson's javadoc. These were JsonMappingException and JsonParseException (though you could go further and pick up other subclasses).

For handling the Camel binding issues, I ended up having things bind through from Camel Headers with the @Header property as strings, and did my own input validation, parsing, etc. That let me throw my own custom exceptions, and hence deal with them in an onException clause.

Not sure if there's a better way, would be nice to know if there is!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM