I use this exception handler to handle some specific exceptions in my Spring boot application (REST API):
#ControllerAdvice
class GlobalExceptionHandler {
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public
#ResponseBody
ResponseMessage notFound(NotFoundException ex) {
return new NotFoundResponseMessage(ex.getMessage());
}
#ResponseStatus(value = HttpStatus.UNSUPPORTED_MEDIA_TYPE)
#ExceptionHandler(HttpMediaTypeNotSupportedException.class)
public
#ResponseBody
ResponseMessage unsupportedMediaType(HttpMediaTypeNotSupportedException ex) {
return new UnsupportedMediaTypeResponseMessage(ex.getMessage());
}
#ExceptionHandler(UnauthorizedException.class)
#ResponseStatus(HttpStatus.UNAUTHORIZED)
public
#ResponseBody
ResponseMessage unauthorized(UnauthorizedException ex) {
return new UnauthorizedResponseMessage(ex.getMessage());
}
#ExceptionHandler(HttpRequestMethodNotSupportedException.class)
#ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
public
#ResponseBody
ResponseMessage methodNotAllowed(HttpRequestMethodNotSupportedException ex) {
return new MethodNotAllowedResponseMessage(ex.getMessage());
}
#ExceptionHandler(ForbiddenException.class)
#ResponseStatus(HttpStatus.FORBIDDEN)
public
#ResponseBody
ResponseMessage forbidden(ForbiddenException ex) {
return new ForbiddenResponseMessage(ex.getMessage());
}
}
and I would like to handle all the others exceptions with one "global" handling method. But I need to get HTTP status code in this method to process error message etc.
Question
Is there some way how to redirect all non-handled exceptions into one particular method? How can I do it?
See the docs:
Any Spring bean declared in the DispatcherServlet’s application
context that implements HandlerExceptionResolver will be used to
intercept and process any exception raised in the MVC system and not
handled by a Controller.
public interface HandlerExceptionResolver {
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
Related
i have a restcontroller with following Code
#RequestMapping(method = RequestMethod.POST, value = "/student")
public void addTopic(#RequestBody Student student) {
student.setPassword(bCryptPasswordEncoder.encode(student.getPassword()));
studentService.addStudent(student);
}
but if the json data doesn't match the Student object, or is wrong formatted an com.fasterxml.jackson.core.JsonParseException: Unexpected character ('"' (code 34)) ist thrown.
what is the best practice to prevent that
I've found that I need to catch JsonProcessingException (which JsonParseException extends from) in the #ExceptionHandler rather than JsonParseException
#ControllerAdvice
public class FeatureToggleControllerAdvice {
#ExceptionHandler(JsonProcessingException.class)
public ResponseEntity<JSONAPIDocument> handleJsonParseException(JsonProcessingException ex) {
final Error error = new Error();
error.setId(UUID.randomUUID().toString());
error.setStatus(HttpStatus.BAD_REQUEST.toString());
error.setTitle(ex.getMessage());
return new ResponseEntity<>(JSONAPIDocument
.createErrorDocument(Collections.singleton(error)), HttpStatus.NOT_FOUND);
}
}
Using JsonParseException in the above sample and nothing is caught, but using JsonProcessingException works as expected.
Use Spring ExceptionHandler to do that
You could specify an ExceptionHandler based on Exception types and also apply the error codes you want to use.
#ExceptionHandler(JsonParseException.class)
public JacksonExceptionHandler {
public ResponseEntity<String> handleError(final Exception exception) {
HttpStatus status = HttpStatus.BAD_REQUEST;
if (exception != null) {
LOGGER.warn("Responding with status code {} and exception message {}", status, exception.getMessage());
return new ResponseEntity<>(exception.getMessage(), status);
}
}
Furthermore you could make use of javax.validation to validate the entity you receive and then Spring Boot will do all the validation automagically. Just add #Valid to the body.
I'm using Bean Validation with RestEasy in Wildfly 8.2.0.Final:
#Path("/user")
#Produces(MediaType.APPLICATION_JSON)
public class UserEndpoint
{
//more code
#GET
#Path("/encrypt/{email}")
public Response fetchEncryptedId(#PathParam("email") #NotNull String email)
{
String encryptedUserId = userService.getEncryptedUserId(email);
return Response.ok().entity(new UserBo(encryptedUserId)).build();
}
}
This basically works. Now I'd like to get the response as JSON object but I can't get it working. All my "application" exceptions are handled by my Exception Mapper, this works:
#Provider
public class DefaultExceptionMapper implements ExceptionMapper<Exception>
{
private static final String MEDIA_TYPE = "application/json";
private LoggingService loggingService;
#EJB
public void setLoggingService(LoggingService loggingService)
{
this.loggingService = loggingService;
}
#Override
public Response toResponse(Exception exception)
{
ResponseObject responseObject = new ResponseObject();
responseObject.registerExceptionMessage(exception.getMessage());
if (exception instanceof ForbiddenException)
{
loggingService.log(LogLevel.ERROR, ((ForbiddenException)exception).getUserId(), ExceptionToStringMapper.map(exception));
return Response.status(Status.FORBIDDEN).type(MEDIA_TYPE).entity(responseObject).build();
}
//more handling
loggingService.log(LogLevel.ERROR, "", ExceptionToStringMapper.map(exception));
return Response.status(Status.INTERNAL_SERVER_ERROR).type(MEDIA_TYPE).entity(responseObject).build();
}
}
But bean validation somehow bypasses it. Then I thought about using Throwable instead of Exception but it didn't help either. I guess the ExceptionMapper is not triggered because there is some life cycle problem with JAX-RS and JSR303. But how can I syncronize them to handle bean validation exceptions?
Additional information: The exception passes the javax.ws.rs.container.ContainerResponseFilter so I could write some workaround by implementing the filter method in a subclass, but this is not clean solution. The target is to handle the exceptions in the Exception mapper.
It's not always the case that your ExceptionMapper<Exception> will catch all exception under the Exception hierarchy. If there is another more specific mapper, say one for RuntimeException, that mapper will be used for all exception of RuntimeException and its subtypes.
That being said (assuming you're using resteasy-validation-provider-11), there is already a ResteasyViolationExceptionMapper that handles ValidationException.
#Provider
public class ResteasyViolationExceptionMapper
implements ExceptionMapper<ValidationException>
This mapper is automatically registered. It returns results in the form of a ViolationReport. The client needs to set the Accept header to application/json in order to see a response similar to
{
"exception":null,
"fieldViolations":[],
"propertyViolations":[],
"classViolations":[],
"parameterViolations":[
{
"constraintType":"PARAMETER",
"path":"get.arg0",
"message":"size must be between 2 and 2147483647",
"value":"1"}
],
"returnValueViolations":[]
}
You can see more at Violation reporting.
If you want to completely override this behavior, you can create a more specific mapper for ResteasyViolationException, which is the exception thrown by the RESTeasy validator
#Provider
public class MyValidationMapper
implements ExceptionMapper<ResteasyViolationException> {
#Override
public Response toResponse(ResteasyViolationException e) {
}
}
While implementing a File Uploader controller in Spring MVC I stucked with one problem. My code snap is given below.
#Controller
public class FileUploader extends AbstractBaseController implements HandlerExceptionResolver
{
#RequestMapping(value = "/uploadFile", method = RequestMethod.POST)
#ResponseBody
public JSONObject handleFileUpload(#RequestParam("file") MultipartFile file)
{
JSONObject returnObj = new JSONObject();
if (file.isEmpty())
{
returnObj.put("success", "false");
returnObj.put("message", "File is empty");
}
else
{
try
{
//my file upload logic goes here
}
catch (Exception e)
{
returnObj.put("success", "false");
returnObj.put("message", "File not uploaded.");
}
}
return returnObj;
}
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object obj, Exception exception)
{
ModelAndView model = new ModelAndView();
Map map = new HashMap();
if (exception instanceof MaxUploadSizeExceededException)
{
// I want to return JSONObject from here like given below.
/**
* { "message":"File size exceeded", "success":"false" }
* */
map.put("message", "File size exceeded");
map.put("success", "false");
model.addObject(map);
}
return model;
}
}
and my spring configuration look likes
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="maxUploadSize" value="300000"/>
</bean>
now In my controller I want to return JSONObject instead of ModelAndView in resolveException method in my controller as given in code snap because I am developing some like REST method to upload file.
any ideas?
Thanks
If you use the Spring 3.2 above, I recommend this way.
At first, declare the ControllerAdvice.
#Controller
#ControllerAdvice
public class JAttachfileApi extends BaseApi
And make the Exception Handler to response JSON Object as following.
#ExceptionHandler(MaxUploadSizeExceededException.class)
public #ResponseBody Map<String,Object> handleMaxUploadSizeExceededException(
MaxUploadSizeExceededException ex)
{
Map<String,Object> result = getResult();
JFileUploadJsonResponse errorResult = new JFileUploadJsonResponse();
errorResult.setError("Maximum upload size of "+ex.getMaxUploadSize()+" bytes exceeded.");
List<JFileUploadJsonResponse> resultData = new ArrayList<JFileUploadJsonResponse>();
resultData.add(errorResult);
result.put("files", resultData);
return result;
}
You simply can annotate the method resolveException as #ExceptionHandler() and then you can have its signature like any other controller method. So placing #ResponseBody before the return type should work.
"Much like standard controller methods annotated with a #RequestMapping annotation, the method arguments and return values of #ExceptionHandler methods can be flexible. For example, the HttpServletRequest can be accessed in Servlet environments and the PortletRequest in Portlet environments. The return type can be a String, which is interpreted as a view name, a ModelAndView object, a ResponseEntity, or you can also add the #ResponseBody to have the method return value converted with message converters and written to the response stream."
I have a Jersey application where I want to prevent the client from seeing any type of stacktrace if any type of Exception occurs.
How do I do this without changing any existing code?
You can register an exception mapper as follows to handle all exceptions and customize the HTTP response:
#Provider
public class MyExceptionMapper implements javax.ws.rs.ext.ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception ex) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
}
i need to catch all exceptions of my controllers to a exception controller. How to configure spring?
I need this because every request to my webapp are json request and in case of exception i need to answer with a genericc {success: false, exception: "String ex..."}. But i can not understand if the better way is to use SimpleMappingExceptionResolver.
Thank you.
If you want to write a custom response, it would be more interesting to use a custom HandlerExceptionResolver implementation.
spring configuration:
<bean id="exceptionHandler" class="com.am.CustomHandlerExceptionResolver"/>
java:
public class CustomHandlerExceptionResolver
implements HandlerExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//write in response
return null;
}
}