Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CXF/ JAX-RS : Return Custom response from interceptor

We need to return custom error code and error message when exception occurs during REST invocation. We have created a exception mapper provider, it works well for the exceptions from the application code. However, it doesn't work when exception occurs from the CXF code (e.g. form the CustomValidationInterceptor that I wrote).

For example, if I request with invalid path parameter (e.g invalid phoneNumber). In this case, we need to return a custom error code and error message in JSON format, but it doesn't work even though we have a exception mapper provider created to handle WebApplicationException.

Is there any way to handle exceptions from cxf interceptors and return response to user with something like the following?

{
"errorDetail": {
"errorCode": "404",
"errorMessage": "Bad Request"
}
}

Code Snippet of my CustomValidationInterceptor :

public class CustomValidationInterceptor extends AbstractPhaseInterceptor<Message>{

    public CustomValidationInterceptor() {
        super(Phase.PRE_INVOKE); // Put this interceptor in this phase
    }

    public void handleMessage(Message message) {

        MetadataMap<String, String> metadataMap = (MetadataMap<String, String>) message.get("jaxrs.template.parameters");

        if(null != metadataMap) {
            List<String> list = metadataMap.get("phoneNumber");
            if(null != list) {
                String phoneNumber = list.get(0);
                boolean result = validatePhoneNumber(phoneNumber);
                if(!result){
                    throw new TelusServiceException(Response.status(Response.Status.BAD_REQUEST).build(), 400, "phone number not valid");
                }
            } else {
                throw new TelusServiceException(Response.status(Response.Status.BAD_REQUEST).build(), 400, "phone number not valid");
            }
        } else {
            throw new TelusServiceException(Response.status(Response.Status.BAD_REQUEST).build(), 400, "phone number not valid");
        }
    }

    public boolean validatePhoneNumber(String phoneNumber) {

          Pattern pattern = Pattern.compile("^[1-9]\\d{9}$");
          Matcher matcher = pattern.matcher(phoneNumber);

          if (!matcher.matches()) {
              return false;
          }
          return true;
     }

}

Code Snippet of my Custom Exception Mapper Provider

public class TelusExceptionHandler implements ExceptionMapper<TelusServiceException> {

    public Response toResponse(TelusServiceException exception) {
        return Response.status(exception.getErrorDetail().getErrorCode()).entity(exception.getErrorDetail()).build();
    }

}

Code Snippet of TelusServiceException

public class TelusServiceException extends WebApplicationException{

// constructors and other methods 

    private ErrorDetail errorDetail = null;

        public ErrorDetail getErrorDetail() {
        return errorDetail;
    }

    public void setErrorDetail(ErrorDetail errorDetail) {
        this.errorDetail = errorDetail;
    }

      public TelusServiceException(Response response, int errorCode, String errorMessage) {
        super(response);

        errorDetail = new ErrorDetail();
        errorDetail.setErrorCode(errorCode);
        errorDetail.setErrorMessage(errorMessage);
    }

}

Code Snippet of ErrorDetail class

@XmlRootElement(name="errorDetail")
public class ErrorDetail {

    private int errorCode;
    private String errorMessage;

    @XmlElement(name = "errorCode")
    public int getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(int errorCode) {
        this.errorCode = errorCode;
    }
    @XmlElement(name = "errorMessage")
    public String getErrorMessage() {
        return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
        this.errorMessage = errorMessage;
    }

}
like image 881
Bhuvan Avatar asked Jun 13 '13 05:06

Bhuvan


2 Answers

I found a way to send a custom response from the interceptor but still can't figure out a way to call my CustomExceptionHandler from the interceptor

Code:

public void handleMessage(Message message) {

        MetadataMap<String, String> metadataMap = (MetadataMap<String, String>) message.get("jaxrs.template.parameters");

        if(null != metadataMap) {
            List<String> list = metadataMap.get("phoneNumber");
            if(null != list) {
                String phoneNumber = list.get(0);
                boolean result = validatePhoneNumber(phoneNumber);
                if(!result){
// Create a response object and set it in the message. 
// calling getExchange() will not call your service
                    Response response = Response
                .status(Response.Status.BAD_REQUEST)
                .entity(new ErrorDetail(Response.Status.BAD_REQUEST.getStatusCode(), Response.Status.BAD_REQUEST.toString()))
                .build();
        message.getExchange().put(Response.class, response);
// That's it
                }
            } else {
                Response response = Response
                .status(Response.Status.BAD_REQUEST)
                .entity(new ErrorDetail(Response.Status.BAD_REQUEST.getStatusCode(), Response.Status.BAD_REQUEST.toString()))
                .build();
        message.getExchange().put(Response.class, response);
            }
        } else {
            Response response = Response
                .status(Response.Status.BAD_REQUEST)
                .entity(new ErrorDetail(Response.Status.BAD_REQUEST.getStatusCode(), Response.Status.BAD_REQUEST.toString()))
                .build();
        message.getExchange().put(Response.class, response);
        }
    }
like image 130
Bhuvan Avatar answered Nov 10 '22 00:11

Bhuvan


I raised a similar question on the cxf user group, see:

http://cxf.547215.n5.nabble.com/Handling-exceptions-in-a-JAX-RS-fault-interceptor-when-using-Local-Transport-td5733958.html

I ended up replacing my interceptors with ContainerRequestFilter and ContainerResponseFilter and then the Exception Mapper happily handled both application exceptions and exceptions thrown from the Filter.

Hope this helps.

like image 38
MandyW Avatar answered Nov 10 '22 00:11

MandyW