Rest Exceptions: Wrappers vs Error Object - java

Lets say we have a rest service defined as:
#GET
#Produces("application/json")
public Response getAllCategories(#QueryParam(value="startIndex") int startIndex, #QueryParam(value="size") int size)
{
logger.info("[SampleCategoryController][getAllCategories]");
List<YpCategory> categoryList = sampleCategoryService.getAllCategories(startIndex, size);
return Response.ok(categoryList).build();
}
and the service is defined as:
public class SampleCategoriesServiceImpl {
public List<YpCategory> getAllCategories(int startIndex, int size) {
...
//call some method that throws a runtime exception
...
}
}
And an Application Exception handler:
#Provider
#Component
public class ApplicationExceptionHandler implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable ex) {
String internalError = "There was a problem processing your request.";
return Response.serverError().entity(new ExceptionResponse(500, internalError)).build();
}
}
}
Exception response object: Let the exception bubble up to the ApplicationExceptionHandler and return the ExceptionResponse Object. This way seems cleaner because the service doesn't have to try to handle an exception that it can't really do anything with and the client will still receive a json response.
Response wrapper: The category object would extend some type of generic response wrapper object with information about error codes then I would always have to wrap the method that can throw a runtime exception in a try/catch block and set the error codes and message info in the catch block.
Is one of these ways preferred? Are there cons to using either one of these methods to handle errors?

I think you should use the ExceptionMapper in this case. It is cleaner to let exceptions be handled outside of your implementation.
Also your implementation should be as less possible aware of HTTP. The less your implementation knows about the other parts of your framework the more flexible it will become.
To give an example. Lets say that in the future there is support for a non-HTTP protocol and error messaging will go different then using HTTP status code. You can do the implementation at the level of ExceptionMapper without changing your implementation. Otherwise you have to refactor your application to be aware of the non-HTTP protocol.
Just to be clear, I don't say there is an other implementation available now. It is just a theory.

Related

Handle exceptions in a rest controller in Spring Boot

I create REST web-service with Spring Boot.
I would like to know what is a better way to handle exceptions in a controller. I have seen other questions and didn’t found an answer.
My controller:
#GetMapping
public ResponseEntity<?> saveMyUser(){
MyUser myUser = new MyUser(“Anna”);
//throws SQLException
MyUserDetails userDetails = userService.saveMyUser(myUser);
//if successful
return ResponseBody.ok(userDetails);
}
saveMyUser() method of UserService:
public MyUserDetails saveUser(MyUser) throws SQLException {...}
So at this point I have at least 2 simple options:
Add exception to method signature.
Here I may rely on Spring Boot to pass all information about exception and status code to a client. However do not know if it is a reliable approach.
Surround with try/catch and pass all information about exceptions manually.
What is a better simple way?
You can create an additional class with #ControllerAdivce annotation and later you will be able to write custom response logic for each exception e.g:
#ControllerAdvice
public class GlobalExceptionHandler {
#ExceptionHandler({SQLException.class})
public ResponseEntity<Object> sqlError(Exception ex) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("Some SQL exception occured");
}
}
Also, you can extend ResponseEntityExceptionHandler and override the default behavior for mapping from exceptions to HTTP response.
Also, take a look at this, it holds very usefull information for your case.

How to obtain http headers in custom path parameter classes?

We use Jersey 1.13. Not my choice, but we cannot update to higher version just yet.
Jersey allows user created java types to consume path parameters. The example given in its documentation is something like this:
Resource method:
#Path("paint/{color}")
#GET
public Response paint(#PathParam("color") ColorParam color) { ... }
Custom java type for path parameter {color}
public class ColorParam {
public ColorParam(String s) {
try {
... // implementation here
} catch (Exception e) {
throw new WebApplicationException("Something's wrong");
}
}
}
I need to localize my response error string - "Something's wrong". For that, I need "Accept-Language" header information.
Is there a way to obtain it inside my ColorParam class somehow?
If this were a resource class, I could have used "#Context HttpHeaders requestHeaders" injection. Can something similar be achieved in my custom class?
Here's the solution I came up with:
My custom class ColorParam throws a custom exception, say InvalidColorException extends WebApplicationException. All the information necessary to build a response except for Locale is stored in this custom exception (like unlocalized error message, HTTP response code etc).
I also created an exception mapper to map InvalidColorException to a desired response that performs localization as it has access to request headers, namely 'Accept-Language' header:
public class InvalidColorExceptionMapper implements ExceptionMapper
This way, creation of ColorParam is completely transparent to all resource classes (assume it's used in quite a few).
I don't think the request parameter class is the correct place to localize the response. This class is creating the parameter instance from the request, it does not create the response.
Check the color instance in paint and throw the WebApplicationException. There you can use all headers.

Handling malformed #QueryParam with RESTEasy

I am a bit confused about the Exception handling or lack thereof in RESTEasy with JBoss AS7/Wildfly. I'm not entirely sure in which "domain" the exception handling falls exactly.
This is what I use for testing:
#GET
#POST
#Path("/test")
#Produces(MediaType.APPLICATION_JSON)
public Response test(#QueryParam("id") final long id) {
log.info("Incoming request! Wee! With id " + id + "!");
return Response.ok().build();
}
So far, so good. That behaves as expected with localhost/app/rest/test?id=123. However, when I put in something that doesn't 'fit' in the parameter, like localhost/app/rest/test?id=123abc, I get a long exception from RESTEasy, correctly informing me that it doesn't fit into the expected parameter.
But what I don't understand is how I can handle/catch this exception. Obviously, I wouldn't want a 40-line stack trace to go to my main (or any) log, but do proper error logging myself. My research only turned out a generic way to handle all Exceptions of type NumberFormatException, which is totally unsuited for any sane logging approach.
So, how can I handle this issue ? As this happens "outside" my code, I can't exactly surround it with try/catch, and a specific bad parameter for a specific REST mapping isn't really something generic enough to write an application wide Exception mapper.
It's quite strange that server doesn't respond with 400 error. If you still want to handle query parameters by yourself you can make a filter and check all query params there.
It shall look like this:
#Provider
public class PreResourceFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
MultivaluedMap<String, String> queryParams = requestContext.getUriInfo().getQueryParameters();
// here you need to check desired parameters
}
}

Spring 3.2 DeferredResult - How to set status code for error response?

Spring Web 3.2 comes with a DeferredResult class for asynchronous request processing. It has a setErrorResult for providing an alternative response if something goes wrong, but no option to supply a http error code.
Surely it must be possible to control the http response code for failed requests.. How do I do that using the new Spring api?
The doc for setErrorResult method says the following:
Set an error value for the DeferredResult and handle it. The value may
be an Exception or Throwable in which case it will be processed as if
a handler raised the exception.
I suppose by setting an Exception, you may trigger an exception handler that returns the code you desire.
deferredResult.setErrorResult(new Exception());
This will always set the HTTP response code to 500. For finer control HttpServletResponse.setStatus seems to work.
This will work with user411180's client side.
public DeferredResult<List<Point>> getMessages(#RequestParam int reqestedIndex,
final HttpServletResponse response) {
final DeferredResult<List<Point>> deferredResult = new DeferredResult<>();
deferredResult.onCompletion(...);
deferredResult.onTimeout(new Runnable() {
#Override
public void run() {
deferredResult.setErrorResult("Explanation goes here.");
response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); //or SC_NO_CONTENT
}
});
longPollRequests.put(deferredResult, reqestedIndex);
return deferredResult;
}
The exception that you pass as the argument to setErrorResult can be
annotated with #ResponseStatus. e.g. create an exception class of your own:
#ResponseStatus(HttpStatus.NOT_FOUND)
class NotFoundException extends RuntimeException {
// add your own constructors to set the error message
// and/or cause. See RuntimeException for valid ctors
}
Then in your code use it with the constructor you have created, for example:
deferredResult.setErrorResult(new NotFoundException(reason, cause));

REST resource exception interceptor

I am trying to determine if it is possible to setup an interceptor like solution on a REST resource such that if an exception is thrown I can log the exception and change the response returned. I basically don't want to wrap all my REST resources with try/catch blocks. If a REST resource was managed I would just use an #Interceptor on all of my calls but since it is not managed that seems to be out of the question.
You can use an implementation javax.ws.rs.ext.ExceptionMapper. Let's suppose that your code might throw a YourFancyException from the resources. Then you can use the following mapper:
#Provider
public class YourFancyExceptionMapper
implements ExceptionMapper <YourFancyException> {
#Override
public Response toResponse(YourFancyException exception) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(exception.getMessage()).build();
}
}
Don't forget to annotate the mapper with #Provider and to make your resources methods to throw YourFancyException.

Categories

Resources