How can I customize exceptions thrown when #Produces and #Consumes is violated - java

I'm working on i18n(Internationalization and localization) task of my
REST services. Now I want to pass the error message according to the
Accept-Language of the header to the Exceptions thrown when Accept or
Content-Type of the header's not matched with #Produces and
#Consumes.
I found a solution in ContainerRequestFilter, but if I
check the Accept and Content-Type of the header in that
ContainerRequestFilter and throw Exception when it's not matched the
MediaType I want, there will be no need to use #Produces and
#Consumes again in the Resource.
So my question is, is there a way to
customize the exceptions thrown when #Produces and #Consumes is
violated (I mean NotAcceptableException and NotSupportedException)?
Because I want to pass the error message in multiple languages to the
message in these Exceptions.

You may use ExceptionMapper which will catch exceptions thrown in your service and return formatted response. It may be customized to handle specific exceptions.
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception> {
#Override
public Response toResponse(final Exception exception){
return Response.status(HttpStatusCodes.STATUS_CODE_SERVER_ERROR).entity(new BasicResponse(InternalStatus.UNHANDLED_EXCEPTION, exception.getMessage())).type(MediaType.APPLICATION_JSON).build();
}
}

Related

Stacktrace of custom exception is not printed in Spring Boot 2.3

Error stacktrace is not printed in console for the custom exception that is annotated with #ResponseStatus
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalErrorException extends RuntimeException {
public InternalErrorException(String message) {
super(message);
}
public InternalErrorException(String message, Throwable throwable) {
super(message, throwable);
}
}
Throwing exception like throw new InternalErrorException("error", e), never get the stacktrace printed in the console unlesss I remove the annotation #ResponseStatus
How could I get it printed while keeping the annotation #ResponseStatus?
See Annotation Type ResponseStatus API doc.
Warning: when using this annotation on an exception class, or when setting the reason attribute of this annotation, the HttpServletResponse.sendError method will be used.
With HttpServletResponse.sendError, the response is considered complete and should not be written to any further. Furthermore, the Servlet container will typically write an HTML error page therefore making the use of a reason unsuitable for REST APIs. For such cases it is preferable to use a ResponseEntity as a return type and avoid the use of #ResponseStatus altogether.
HttpServletResponse.sendError does not throw your error and I guess it is never logged because of that.
Maybe you want to implement exception handler for that exception to get it logged.
Related question

Spring ExceptionHandler but for normal beans

I have been able to successfully use #ExceptionHandler annonated methodsorg.springframework.web.bind.annotation.ExceptionHandler in Controller Classes in my Spring projects to handle exceptions thrown by spring #RestController
Working example:
#Validated
#RestController
#RequestMapping(value = UrlsProperties.API_PATH, produces = MediaType.APPLICATION_JSON, consumes = { MediaType.APPLICATION_JSON })
#Api(value = "MyController", description = "MyController processing and forwarding controller")
public class MyController {
private static Logger log = LogManager.getLogger(MyController.class);
...
#JsonFormat
#ExceptionHandler(MissingServletRequestParameterException.class)
public ResponseMessage handleMissingParams(MissingServletRequestParameterException ex) {
String name = ex.getParameterName();
log.error(name + " parameter is missing");
return new ResponseMessage(400, ex.getMessage());
}
}
I am trying to achieve the same way of exception handling but for a normal bean, [ not a controller ]. Simply adding an #ExceptionHanlder annotated method did not seem to catch the exceptions thrown by that bean's methods.
My question is how to handle exceptions thrown by a bean by writing a method inside this bean?
#ExceptionHandler annotation is not for general exception handling. It's used in controllers to convert an exception into a proper HTTP response. It won't work for normal beans, because only controllers return a response.
If any code (doesn't need to be in a bean) throws an exception and you don't handle it, it would eventually propagate up to your controller's exception handler and it would be converted to a response. That would be poor design though, as you should handle exceptions as early as you can.
What you can do is create exceptions that are meant to be propagated to your exception handlers. Your code catches an exception, then re-throws it wrapped into your own exception (such as IllegalRequestException). The handler then returns an error code and details to the caller.

Custom WebApplicationExceptions in jaxrs caught by ExceptionMapper show up in server log

I use the following Exceptionmapper to map WebApplicationExceptions in my jaxrs rest api to responses.
#Provider
public class ErrorHandler implements ExceptionMapper<WebApplicationException> {
#Override
public Response toResponse(WebApplicationException e) {
int status = e.getResponse().getStatus();
JsonObject errorResponse = Json.createObjectBuilder()
.add("status", status)
.add("message", e.getMessage())
.build();
return Response.status(status)
.entity(errorResponse)
.type(MediaType.APPLICATION_JSON)
.build();
}
}
This works fine and it does exactly what it should do, but when I throw custom errors, for example throw new NotFoundException("custom message"); the stacktrace shows up in my server log. Can anyone explain this? Or does anyone know of a solution?
TL;DR;
For some reason when I throw WebApplicationExceptions from my jax-rs code, my ExceptionMapper handles the error but still throws it so it shows up in the server log.
Any solutions?
I've found the origin of this problem and managed to solve it.
From the JAX-RS spec
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
In my ExceptionMapper I used the WebApplicationException, so every error would be mapped. The problem is that WebApplicationException is not the nearest superclass of (e.g.) NotFoundException. There is a ClientErrorException superclass inbetween. When I changed my mapper to that class the problem was solved.
Since I only want to map client errors to Json responses this solution works fine for me.

Serializing Jersey exceptions from filter throws error

I currently have a RESTful webservice running Jersey. I recently added a filter that does some auth stuff, and it works in the happy-path case. However, when I need to throw an error from within this filter, instead of serializing the exception into a pretty json string it, it throws a 500 with the following error:
javax.ws.rs.WebApplicationException: com.sun.jersey.api.MessageException: A message
body writer for Java class myclass, and Java type class myclass, and MIME media type
application/octet-stream was not found
The thing is, I don't want to write anything to application/octet-stream. My service only uses application/json. This is not a problem in my actual Resource classes, where I can specify the#Produces annotation. Error responses thrown from the body of a resource will serialize properly.
My question, then, is: How do I control what MIME type is used for exceptions thrown while filtering?
You need to build an exceptionmapper to handle the exceptions and turn them in to JSON. Something like:
#Provider
public class UnexpectedExceptionMapper implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(final Exception exception)
{
ResponseBuilder builder = Response.status(Status.BAD_REQUEST)
.entity(jsonError(exception))
.type(MediaType.APPLICATION_JSON);
return builder.build();
}
private String jsonError(final Exception exception)
{
return "{\"error\":\"" + exception.getMessage() + "\"}";
}
}

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