#ControllerAdvice doesn't handle exceptions - java

I am using Spring Boot 1.5.9 for developing my application. I need implement jwt authentication, and I used jjwt library. The following code is from my custom authentication security filter which inherits from OncePerRequestFilter. Here I tried to parse the username from token, when username is parsing automatically is jwt verified and also check expiration of token. I debug it and it works, so I next want to send the correct message to the client app why authentication failed. I want to throw an ExpiredJwtException and handle it with the controller advice where I format the output.
Here is exception throwing:
try {
username = jwtTokenService.getUsername(authToken);
} catch (IllegalArgumentException e) {
logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
logger.warn("the token is expired and not valid anymore", e);
throw new ExpiredJwtException(e.getHeader(), e.getClaims(), e.getMessage());
}
And here is my controller Advice, JwtException is base class of ExpiredJwtException which I throw so it should work. I also tried directly use ExpiredJwtException in ExceptionHandler, but didn't work as well. Next I want to handle another exceptions with same way.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(Exception.class)
public #ResponseBody
ResponseEntity<Map<String, Object>> handleException(Exception ex) {
Map<String, Object> errorInfo = new HashMap<>();
errorInfo.put("message", ex.getMessage());
errorInfo.put("status", HttpStatus.BAD_REQUEST);
errorInfo.put("status_code", HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(JwtException.class)
//#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public #ResponseBody
ResponseEntity handleJwtException(JwtException ex) {
Map<String, Object> errorInfo = new HashMap<>();
errorInfo.put("message", ex.getLocalizedMessage());
errorInfo.put("status", HttpStatus.UNPROCESSABLE_ENTITY);
errorInfo.put("status_code", HttpStatus.UNPROCESSABLE_ENTITY.value());
return new ResponseEntity<>(errorInfo, HttpStatus.UNPROCESSABLE_ENTITY);
}
}
Here is my folder structure:
I want return just response with 4xx status, but I always got 5xx Internal error when my exception is thrown. Can you tell me what is wrong with my code? Thanks in advice.

If the exception is thrown in filter, Springs exception handling (#ControllerAdvice, #ExceptionHandler) is not involved.
You need to catch all exceptions inside filter and work directly with ServletResponse.
As I understand - Filters are low level logic (request handling before spring infrastructure), but you can have a workaround, like a specific filter that wraps chaining and catches all RuntimeExceptions. (Looks like a crunch, but no other solutions).
If you want to have a specific login to create your exception object - override ErrorAttributes bean. It will allow you to have a single view for all application exceptions.
To directly specify http response status usehttpServletResponse.setStatus(... your status code ...);

Have your controller extend ResponseEntityExceptionHandler and have your exception handling methods take in the WebRequest as a parameter
Then change your return value to this
return handleExceptionInternal(ex, errorInfo, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
The HttpStatus.BAD_REQUEST can be changed to any 40x error
Example for Exception.class
#ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleUncaughtException(Exception ex, WebRequest request) {
String message = "Something bad happened";
return handleExceptionInternal(ex, message, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
According to this Make simple servlet filter work with #ControllerAdvice you can create a custom handler.
Then add your new handler to your WebSecurityConfigurerAdapter
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomHandler());
}

I also faced this issue in which RestControllerAdivce was not handling the exception, Thing is that advice method can have only those arguments in its signature which exception throwing method have or can provide. My AOP method was not having access to Headers so it could not provide Headers to RestControllerAdivce method. As soon as I created a new exception handler method in RestController without Headers as argument, RestControllerAdivce started working as expected. Detials here

Related

Spring Security 400

if you specify incorrect data when logging in, postman gives the message "invalid data", but the status is "200 ok", I need to receive 400 status
#PostMapping("/login")
public Map<String, Object> performLogin(#RequestBody PersonDTO personDTO, BindingResult bindingResult) throws BadCredentialsException{
UsernamePasswordAuthenticationToken authInputToken =
new UsernamePasswordAuthenticationToken(personDTO.getUsername(),
personDTO.getPassword());
try {
authenticationManager.authenticate(authInputToken);
}
catch (BadCredentialsException e) {
return Map.of("message", "Неверные данные!");
}
String token = jwtUtil.generateToken(personDTO.getUsername());
Optional<Person> person = peopleRepository.findByUsername(personDTO.getUsername());
You can declare global exception handler, then you won't need to catch the exception in your controller at all.
#ControllerAdvice
public class ErrorHandler {
#ExceptionHandler(BadCredentialsException.class)
public ResponseEntity<Map<String, Object>> badCredentialsHandler() {
return ResponseEntity.badRequest().body(Map.of("message", "invalid credentials"));
}
}
Keep in mind the exception is quite general, so depending on your use case you may still need to catch and rethrow it wrapped in some custom exception and then handle the custom one.
If the exception contains information you need to build the response, or just for logging purposes, you can autowire it in the handler method.

How to create custom "Request Body is Missing" error in Spring Boot

To create custom error, we need to know what kind of Exception error it is. The problem is I cannot determine what kind of error the "request body is missing one" is. At first I thought it was categorized as MethodArgumentNotValidException , but it doesn't catch the error.
I create the Controller Advice for the error
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException exception, HttpHeaders headers, HttpStatus status, WebRequest request){
MyObject<Object> error = MyObject.failure("Invalid Parameter");
log.error("argument invalid", exception);
return new ResponseEntity<Object>(error, new HttpHeaders(), HttpStatus.OK);
}
The controller
#PostMapping(value = "/tes")
public MyObject<MyRes> myTest(#Valid #RequestBody MyReq req, HttpServletRequest hsReq) throws Exception{
return myService.updateTestData(req);
}
I used Postman to call the API.
* First Trial with bracket
* Second Trial - without bracket
No error occurred.
My question is, how to handle this error, When no request body attached at all in the request. I want to return the "invalid param" error in this case too.
This might be late but you might want to try adding this on your ControllerAdvice class, that's how I caught the Required request body is missing error when trying to send an empty request.
#ExceptionHandler(HttpMessageNotReadableException.class)
public ResponseEntity<Object> handleMissingRequestBody(HttpMessageNotReadableException ex) {
return new ResponseEntity<Object>(ex.getMessage(), HttpStatus.BAD_REQUEST);
}

Spring REST add field to 404 resonse code

Using latest Spring Boot as of May 2018. I've created a 404 response like this.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
private final int errorId;
public NotFoundException(String errorMsg) {
super("-1," + errorMsg);
this.errorId = -1;
}
public NotFoundException(int errorId, String errorMsg) {
super(errorId + "," + errorMsg);
this.errorId = errorId;
}
public int getErrorId() {
return errorId;
}
}
The annotation #ResponseStatus(HttpStatus.NOT_FOUND) makes my NotFoundException appear like a 404 reponse like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"1000,Could not find data for owner: 1234","path":"/resource/owner/1234"
}
I hoped that property "getErrorId" would appear in the response automatically, like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"Could not find data for owner: 1234","path":"/resource/owner/1234",
"errorId": 1000
}
Is the a simply way (like an annotiation to the getErrorId method) of having the property "errorId" in the response?
You use #ControllerAdvice and #ExceptionHanlder in Spring. that is exception controller. In fact, you will make custom exception controller and define exception.
This is sample code for you :
#ControllerAdvice("your.package")
public class CommonExceptionHandler {
#ExceptionHandler(value = NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public #ResponseBody ResponseEntity<?> setNotFoundException(Exception exception) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// this is sample map. you will make your custom model and you use exception parameter.
Map<String, String> map = new HashMap<String, String>();
map.put("timestamp", String.valueOf(new Date().getTime()));
map.put("status", HttpStatus.NOT_FOUND.toString());
map.put("error", "Not Found");
map.put("exception", exception.getMessage());
map.put("message", "Could not find data for owner: 1234");
map.put("path", "/resource/owner/1234");
map.put("errorId", "1000");
String json = mapper.writeValueAsString(map);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(json);
}
}
what ever Byeon0gam told everything is fine, here i am going to show another way means little bit of difference in maintaining code.
We know already ,
we can handle exceptions in spring-rest by 4 ways:
1. Using ResponseEntity Class.
2. Using #ResponseStatus Annotation.
3. Using #ExceptionHandler() Annotation.
4. Return Error Representation instead of default HTML error Page.
By using Those we can handle Exceptions at Method or Class level only.
But, if you want to handle Globally means throughout application , please follow below steps.
Handling Global Exception:
To Handle all Exceptions in our applications ,
First we need to create a class, after we need to use #ControllerAdvice Annotation on top of a class. In that class body , we can handle the exceptions raised in our application.
In that Class , we will create Exception handling methods , on top of every method we will use #ExceptionHandler() annotation for navigating Exceptions and for Handling .
If any exception raises in our application , based on #ExceptionHandler(“argument”) annotation argument the exception hadling method will be invoked and remaining handling code will be excuted.
#ControllerAdvice
public class SpringRestGlobalExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<?> exceptionHandler(HttpServletRequest req, Exception e)
{
JSONObject obj =new JSONObject();
obj.put("msgTxt","Unknown Server Error, Please Contact Admin." );
obj.put("reqUrl", req.getRequestURI());
obj.put("stackTrace", e.toString());
obj.put("isErrorFlag", true);
obj.put("httpStatusCode", HttpStatus.OK.value());
gstcDaoi.saveExceptionOrErrorLog(prepareTGstcExceptionOrErrorLogObject(obj));
e.printStackTrace();
return new ResponseEntity<>(obj, HttpStatus.OK);
}

#ExceptionHandler for type Exception not called for Spring exception in custom ResponseEntityExceptionHandler

After reading some blog posts about making a custom exception handler for Spring, I wrote the following class:
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = Exception.class)
#ResponseBody
public ResponseEntity<Object> exceptionHandler(Exception e) {
HashMap<String, Object> msg = new HashMap<>(2);
msg.put("error", HttpStatus.PRECONDITION_FAILED.value());
msg.put("message", "Something went wrong");
return new ResponseEntity<>(msg, HttpStatus.BAD_REQUEST);
}
}
The intent is to send msg in the JSON response instead of giving away the Spring exception what was thrown for whatever reason.
This class isn't working, however.
When I hit, say, and invalid endpoint for my server API, I get the default response payload:
{
"timestamp": 1449238700342,
"status": 405,
"error": "Method Not Allowed",
"exception": "org.springframework.web.HttpRequestMethodNotSupportedException",
"message": "Request method 'POST' not supported",
"path": "/bad_enpoint"
}
What am I missing?
Thanks.
Your handler will not be called because you want to map Exception to your custom error response but Spring MVC most likely already has one exception handler registered for Exception class. It also has one that handles HttpRequestMethodNotSupportedException for sure.
It is not a great idea however, to overwrite entire Spring MVC exception handling/mapping anyway. You should only care about specific exceptions - ones that you define.
Please read this article for a bit more insight into Spring MVC exception handling.
You don't need to extend ResponseEntityExceptionHandler to make it work.
Setting two HttpStatuses is reaaaaaly bad idea.
#ControllerAdvice
public class RestExceptionHandler {
#ExceptionHandler(value = Exception.class)
#ResponseBody
public ResponseEntity<String> exceptionHandler(Exception e) {
return new ResponseEntity<>("Something went wrong", HttpStatus.BAD_REQUEST);
}
}

SpringMVC handle errors in rest controller

I'm using SpringMVC and I want to handle exception on rest controller.
My controller usually write a json in response output, but when exception occurs I'm unable to catch it and tomcat html page is returned.
How I can catch global exceptions and return appropriate response based on "accept" parameter in request?
The #ControllerAdvice annotation is a new annotation that was added in the Spring 3.2 release. From the reference docs:
Classes annotated with #ControllerAdvice can contain #ExceptionHandler, #InitBinder, and #ModelAttribute methods and those will apply to #RequestMapping methods across controller hierarchies as opposed to the controller hierarchy within which they are declared. #ControllerAdvice is a component annotation allowing implementation classes to be auto-detected through classpath scanning.
Example:
#ControllerAdvice
class GlobalControllerExceptionHandler {
// Basic example
#ExceptionHandler
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
ErrorMessage handleException(FirstException ex) {
ErrorMessage errorMessage = createErrorMessage(ex);
return errorMessage;
}
// Multiple exceptions can be handled
#ExceptionHandler({SecondException.class, ThirdException.class})
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
ErrorMessage handleException() {
ErrorMessage errorMessage = createErrorMessage(...);
return errorMessage;
}
// Returning a custom response entity
#ExceptionHandler
ResponseEntity<ErrorMessage> handleException(OtherException ex) {
ErrorMessage errorMessage = createErrorMessage(...);
ResponseEntity<ErrorMessage> responseEntity = new ResponseEntity<ErrorMessage>(errorMessage, HttpStatus.BAD_REQUEST);
return responseEntity;
}
}
Basically, it allows you to catch the specified exceptions, creates a custom ErrorMessage (this is your custom error class that Spring will serialize to the response body according to the Accept header) and in this example sets the response status to 400 - Bad Request. Note that the last example returns a ResponseEntity (and is not annotated with #ResponseBody) which allows you to specify response status and other response headers programmatically. More information about the #ExceptionHandler can be found in the reference docs, or in a blog post that I wrote some time ago.
Update: added more examples based on comments.
Another approach (what I'm using) is to create a global exception handler and tell Spring that it should be used. Then you don't have to duplicate your logic or to extend the same base-controller as you have to do when annotating a controller method with #ExceptionHandler. Here's a simple example.
public class ExceptionHandler implements HandlerExceptionResolver {
#Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse response, Object o, Exception e) {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); // Or some other error code
ModelAndView mav = new ModelAndView(new MappingJackson2JsonView());
mav.addObject("error", "Something went wrong: \"" + e.getMessage() + "\"");
return mav;
}
}
And in the <something>-servlet.xml you assign it as your wanted exceptionResolver:
<!-- Define our exceptionHandler as the resolver for our program -->
<bean id="exceptionResolver" class="tld.something.ExceptionHandler" />
Then all exceptions will be sent to your Exceptionhandler, and there you can take a look at the request and determine how you should reply to the user. In my case I'm using Jackson.
the ExceptionHandler annotation is doing hwat you want.

Categories

Resources