I am using ControllerAdvice to handle exceptions in my spring boot application.
#Order(Ordered.HIGHEST_PRECEDENCE)
#ControllerAdvice
public class ErrorApiHandler extends ResponseEntityExceptionHandler {
final
ResponsesHelper rh;
public ErrorApiHandler(ResponsesHelper rh) {
this.rh = rh;
}
#ExceptionHandler(UsernameNotFoundException.class)
public ResponseEntity<Object> handleUsernameNotFoundException(UsernameNotFoundException ex) {
log.error(ExceptionUtils.getStackTrace(ex));
var error = buildError(ex);
return rh.buildResponse(error, HttpStatus.NOT_FOUND);
}
...
}
It works fine for exceptions thrown within my controllers.
However, with exceptions thrown, for example within a service the ControllerAdvice is not executed.
#Service
public class CustomUserDetailsService implements UserDetailsService {
final
UserRepository userRepository;
public CustomUserDetailsService(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Override
#Transactional
public User loadUserByUsername(String email)
throws UsernameNotFoundException {
log.debug(String.format("Loading user %s", email));
User user = userRepository.findByEmail(email)
.orElseThrow(() -> {
log.debug(String.format("User %s not found", email));
return new UsernameNotFoundException("User not found : " + email); // <- This exception is not handled.
});
log.debug(String.format("User %s loaded", user));
return user;
}
How can I handle all exceptions thrown within my application?
Thanks in advance.
I found this in ResponseEntityExceptionHandler docs:
A convenient base class for #ControllerAdvice classes that wish to provide centralized exception handling across all #RequestMapping methods through #ExceptionHandler methods.
It seems that a custom exception handler that extends that class will only handle exceptions in the controller layer.
I found this tutorial - a solution that uses HandlerExceptionResolver sounds like the one you are looking for.
#ControllerAdvice is meant to handle exceptions that propagate through controller methods (that are thrown from within controller method calls - including bubbling exceptions). So whenever you directly throw exceptions in your controller method or something that controller method is calling throws an exception, an attempt to handle it through advice will be made.
If you want your exceptions to be handled (somehow) outside of web context with a similar manner, your will have to write your own aspect that will literally wrap everything try-catch and will let you handle the exception.
In my case my "CustomUserDetailsService" is called inside a filter:
public class JwtAuthenticationFilter extends OncePerRequestFilter {
#Autowired
private JwtTokenProvider tokenProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
protected void doFilterInternal(
HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain
) throws ServletException, IOException {
try {
String jwt = getJwtFromRequest(request);
if (StringUtils.hasText(jwt) && tokenProvider.validateToken(jwt)) {
UUID userId = tokenProvider.getUserIdFromJWT(jwt);
UserDetails userDetails = customUserDetailsService.loadUserById(userId);
...
I handled it by catching the exceptions there:
catch (UsernameNotFoundException ex){
log.error(ExceptionUtils.getStackTrace(ex));
var apiError = ErrorApiHandler.buildError(
new ResourceAlreadyTakenException(ex.getMessage())
);
response.addHeader("Content-Type", "application/json");
response.setStatus(HttpStatus.BAD_REQUEST.value());
PrintWriter writer = response.getWriter();
writer.write(convertObjectToJson(apiError));
writer.flush();
return;
}
Ideally, handle all exceptions with the controller advice, but this way it works
Related
I have a simple Spring Boot 2.1 application with a Spring Interceptor and #RestControllerAdvice.
My requirement is to have the Spring Interceptor be called for all situations, including when an exception occurs.
For custom exceptions, the Interceptor handler methods do get called, e.g. preHandle() and afterCompletion(). However, for exceptions handled by ResponseEntityExceptionHandler, the Spring Interceptor does not get called (I need ResponseEntityExceptionHandler's methods to create a custom ResponseBody to send back, however, I also need to trigger Interceptor's afterCompletion() for auditing purposes).
For instance, if a REST request is made with PATCH HTTP method, it only executes PersonControllerExceptionHandler.handleHttpRequestMethodNotSupported() and no PersonInterceptor is invoked.
Exception Handler:
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonControllerExceptionHandler extends ResponseEntityExceptionHandler {
private final static Logger LOGGER = LoggerFactory.getLogger(PersonControllerExceptionHandler.class);
#ExceptionHandler(value = {PersonException.class })
public ResponseEntity<Object> handlePersonException(PersonException exception) {
LOGGER.info("Person exception occurred");
return new ResponseEntity<Object>(new Person("Bad Age", -1),
HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(value = {Exception.class })
public ResponseEntity<Object> handleException(Exception exception) {
LOGGER.info("Exception occurred");
return new ResponseEntity<Object>(new Person("Unknown Age", -100),
HttpStatus.INTERNAL_SERVER_ERROR);
}
#Override
public ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
LOGGER.info("handleHttpRequestMethodNotSupported()...");
return new ResponseEntity<>(new Person("Argh!", 900), HttpStatus.METHOD_NOT_ALLOWED);
}
}
The Interceptor:
#Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(PersonInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
LOGGER.info("PersonInterceptor#preHandler()...");
return true;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
LOGGER.info("PersonInterceptor#postHandler()...");
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
LOGGER.info("PersonInterceptor#afterCompletion()...");
if (ex != null) {
LOGGER.error("afterCompletion(): An exception occurred", ex);
}
}
}
Registering the Interceptor:
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PersonInterceptor()).addPathPatterns("/person/*");
}
}
Controller:
#RestController
#RequestMapping("/")
public class PersonController {
private final static Logger LOGGER = LoggerFactory.getLogger(PersonController.class);
#Autowired
private PersonService personService;
#GetMapping(path = "/person/{age}", produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPerson(#PathVariable("age") Integer age) throws PersonException {
LOGGER.info("Age: {}", age);
return personService.getPerson(age);
}
}
Initially I thought it has something to do with #Ordered but trying various scenarios where I give PersonInterceptor a higher precedence than #RestControllerAdvice yields the same undesirable outcome (or vice versa).
After digging into Spring framework, it seems like if a handler is not found, an exception is thrown back to DispacherServlet#doDispatch() which goes into a catch block, and therefore, it skips interceptor mapping process, including the afterCompletion() (I'm using Spring 5.1. as an example to trace the execution path):
DispacherServlet#doDispatch() is called and attempts is made to get the HandlerExecutionChain
I can see there are several HandlerMapping's; the one that fails is RequestMappingHandlerMapping
In AbstractHandlerMapping#getHandler(), it tries to get the handler via AbstractHandlerMethodMapping#getHandlerInternal()
Eventually, AbstractHandlerMethodMapping#lookupHandlerMethod() is called which fails to find a matching pattern due to the fact that there is no PATCH getPerson(), but rather GET getPerson()
At this point, RequestMappingInfoHandlerMapping#handleNoMatch() throws HttpRequestMethodNotSupportedException
This exception bubbles up to DispatcherServlet#doDispatch() exception clause which then processes by the exception resolver that it finds in
DispatcherServlet#processHandlerException() (of course, this finds an exception resolver and doesn't throw an exception which might trigger DispatcherServlet#triggerAfterCompletion() when an exception is caught in DispacherServlet#doDispatch() exception clause
Is there something I am missing to trigger the interceptor's afterCompletion() in cases when there is no handler match?
Currently, whenever a user fails authentication, spring security responds with:
{"error": "invalid_grant","error_description": "Bad credentials"}
And I would like to enhance this response with a response code like:
{"responsecode": "XYZ","error": "invalid_grant","error_description": "Bad credentials"}
After some poking around, it looks like what I need to do this is implement an AuthenticationFailureHandler, which I have begun to do. However, the onAuthenticationFailure method never seems to be reached whenever I submit invalid login credentials. I have stepped through the code, and placed logging in the onAuthenticationFailure method to confirm it is not being reached.
My failure handler is:
#Component
public class SSOAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
response.addHeader("responsecode", "XYZ");
}
}
And my WebSecurityConfigurerAdapter contains:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired SSOAuthenticationFailureHandler authenticationFailureHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.formLogin().failureHandler(authenticationFailureHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(service).passwordEncoder(passwordEncoder());
auth.authenticationEventPublisher(defaultAuthenticationEventPublisher());
}
#Bean
public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(){
return new DefaultAuthenticationEventPublisher();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SSOAuthenticationFailureHandler authenticationHandlerBean() {
return new SSOAuthenticationFailureHandler();
}
#Bean
public PasswordEncoder passwordEncoder(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
My questions are:
Is this the right way to achieve the result I want? (Customizing the spring security authentication response)
If so, did I do something wrong trying to set up my authentication failure handler (since a bad login doesn't seem to be reaching the onAuthenticationFailure method?
Thank you!
You can add exception handling to you Spring Security by calling .exceptionHandling() on your HttpSecurity object in your configure method.
If you only want to handle just bad credentials you can ignore the .accessDeniedHandler(accessDeniedHandler()).
The access denied handler handles situations where you hav secured you app at method level such as using the #PreAuthorized, #PostAuthorized, & #Secured.
An example of your security config could be like this
SecurityConfig.java
/*
The following two are the classes we're going to create later on.
You can autowire them into your Security Configuration class.
*/
#Autowired
private CustomAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
/*
Adds exception handling to you HttpSecurity config object.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.exceptionHandling()
.authencationEntryPoint(unauthorizedHandler) // handles bad credentials
.accessDeniedHandler(accessDeniedHandler); // You're using the autowired members above.
http.formLogin().failureHandler(authenticationFailureHandler);
}
/*
This will be used to create the json we'll send back to the client from
the CustomAuthenticationEntryPoint class.
*/
#Bean
public Jackson2JsonObjectMapper jackson2JsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return new Jackson2JsonObjectMapper(mapper);
}
CustomAuthenticationEntryPoint.java
You can create this in its own separate file. This is Entry point handles the invalid credentials.
Inside the method we'll have to create and write our own JSON to the HttpServletResponse object. We'll
use the Jackson object mapper bean we created in the Security Config.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
#Autowired // the Jackson object mapper bean we created in the config
private Jackson2JsonObjectMapper jackson2JsonObjectMapper;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException {
/*
This is a pojo you can create to hold the repsonse code, error, and description.
You can create a POJO to hold whatever information you want to send back.
*/
CustomError error = new CustomError(HttpStatus.FORBIDDEN, error, description);
/*
Here we're going to creat a json strong from the CustomError object we just created.
We set the media type, encoding, and then get the write from the response object and write
our json string to the response.
*/
try {
String json = jackson2JsonObjectMapper.toJson(error);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().write(json);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
CustomAccessDeniedHandler.java
This handles authorization errors such as trying to access method without the
appropriate priviledges. You can implement it in the same way we did above with the bad credentials exception.
#Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
// You can create your own repsonse here to handle method level access denied reponses..
// Follow similar method to the bad credentials handler above.
}
}
Hopefully this is somewhat helpful.
We have a requirement to verify whether a username exists in database and then authenticate against AD. If username doesn’t exist application will return error instead of trying to authenticate against AD. I have authenticated against multiple AD’s and/or database but I have trouble getting this to work. Any hints would be helpful. Thank you
In my class that extends WebSecurityConfigurerAdapter I tried to play with authenticationProvider where I could verify the existence in DB. But not sure of what to return so that the authentication could be proceed to LDAP.
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(authenticationProvider)
.authenticationEventPublisher(authenticationEventPublisher)
.ldapAuthentication()
.....;
}
I also tried adding a before/after filter but not successful in there either
#Override
protected void configure(HttpSecurity http) throws Exception {
http
....
.and()
.addFilterBefore(preAuthenticationFilter, UsernamePasswordAuthenticationFilter.class)
;
In the filter preAuthenticationFilter the instance of request passed in doFilter() method FirewalledRequest. From this instance I am unable to get the username; looks like this is by design. If anyone has any advice on how we could retrieve username from the instance of FirewalledRequest please share it here. I will give it a try.
So instead of using the filter I decided to play with the custom AuthenticationProvider. In the AuthenticationProvider implementation under the method authenticate() I return null (and log, notify, etc.) when user exist. If user doesn’t exits I return the same instance of authentication passed. This breaks the chain and stops proceeding to authenticating against AD. Throwing any instance of AuthenticationException doesn’t work as Spring security captures this exception and proceeds further (per docs).
Here is how the code snippet looks like
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException
{
Optional<User> user = service.findUserByUsername((String) authentication.getPrincipal());
if (user.isPresent())
return null;
return authentication;
}
Please share any better ideas.
#gandr solution got me thinking, and my final solution was:
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.authenticationProvider(PreLdapAuthenticationProvider)
// authenticationEventPublisher not used anymore
.ldapAuthentication()
.....;
private static class PreLdapAuthenticationProvider implements AuthenticationProvider {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
this.userService.checkUserEnabled((String) authentication.getPrincipal());
return null;
}
}
public class UserService {
public void checkUserEnabled(String username) throws AuthenticationException {
UserEntity entity = userRepository.findByLogin(username);
if (entity == null) {
throw new PreLdapUsernameNotFoundException();
// normal UsernameNotFoundException extends AuthenticationException
// and will be caught and ignore, but not if my custom class
// extends AccountStatusException
}
if (!entity.isEnabled()) {
throw new DisabledException("DisabledException");
}
}
}
public class PreLdapUsernameNotFoundException extends AccountStatusException {
public PreLdapUsernameNotFoundException() {
super("PreLdapUsernameNotFoundException");
}
}
Then you can catch PreLdapUsernameNotFoundException and InternalAuthenticationServiceException in
private AuthenticationFailureHandler authenticationFailureHandler() {
return (request, response, authenticationException) -> {
if (authenticationException instanceof PreLdapUsernameNotFoundException){
...
} else ...
}
}
Currently, whenever a user fails authentication, spring security responds with:
{"error": "invalid_grant","error_description": "Bad credentials"}
And I would like to enhance this response with a response code like:
{"responsecode": "XYZ","error": "invalid_grant","error_description": "Bad credentials"}
After some poking around, it looks like what I need to do this is implement an AuthenticationFailureHandler, which I have begun to do. However, the onAuthenticationFailure method never seems to be reached whenever I submit invalid login credentials. I have stepped through the code, and placed logging in the onAuthenticationFailure method to confirm it is not being reached.
My failure handler is:
#Component
public class SSOAuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
super.onAuthenticationFailure(request, response, exception);
response.addHeader("responsecode", "XYZ");
}
}
And my WebSecurityConfigurerAdapter contains:
#Configuration
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired SSOAuthenticationFailureHandler authenticationFailureHandler;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.formLogin().failureHandler(authenticationFailureHandler);
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(service).passwordEncoder(passwordEncoder());
auth.authenticationEventPublisher(defaultAuthenticationEventPublisher());
}
#Bean
public DefaultAuthenticationEventPublisher defaultAuthenticationEventPublisher(){
return new DefaultAuthenticationEventPublisher();
}
#Override
#Bean
public AuthenticationManager authenticationManagerBean() throws Exception {
return super.authenticationManagerBean();
}
#Bean
public SSOAuthenticationFailureHandler authenticationHandlerBean() {
return new SSOAuthenticationFailureHandler();
}
#Bean
public PasswordEncoder passwordEncoder(){
PasswordEncoder encoder = new BCryptPasswordEncoder();
return encoder;
}
}
My questions are:
Is this the right way to achieve the result I want? (Customizing the spring security authentication response)
If so, did I do something wrong trying to set up my authentication failure handler (since a bad login doesn't seem to be reaching the onAuthenticationFailure method?
Thank you!
You can add exception handling to you Spring Security by calling .exceptionHandling() on your HttpSecurity object in your configure method.
If you only want to handle just bad credentials you can ignore the .accessDeniedHandler(accessDeniedHandler()).
The access denied handler handles situations where you hav secured you app at method level such as using the #PreAuthorized, #PostAuthorized, & #Secured.
An example of your security config could be like this
SecurityConfig.java
/*
The following two are the classes we're going to create later on.
You can autowire them into your Security Configuration class.
*/
#Autowired
private CustomAuthenticationEntryPoint unauthorizedHandler;
#Autowired
private CustomAccessDeniedHandler accessDeniedHandler;
/*
Adds exception handling to you HttpSecurity config object.
*/
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf()
.disable()
.exceptionHandling()
.authencationEntryPoint(unauthorizedHandler) // handles bad credentials
.accessDeniedHandler(accessDeniedHandler); // You're using the autowired members above.
http.formLogin().failureHandler(authenticationFailureHandler);
}
/*
This will be used to create the json we'll send back to the client from
the CustomAuthenticationEntryPoint class.
*/
#Bean
public Jackson2JsonObjectMapper jackson2JsonObjectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
return new Jackson2JsonObjectMapper(mapper);
}
CustomAuthenticationEntryPoint.java
You can create this in its own separate file. This is Entry point handles the invalid credentials.
Inside the method we'll have to create and write our own JSON to the HttpServletResponse object. We'll
use the Jackson object mapper bean we created in the Security Config.
#Component
public class CustomAuthenticationEntryPoint implements AuthenticationEntryPoint, Serializable {
private static final long serialVersionUID = -8970718410437077606L;
#Autowired // the Jackson object mapper bean we created in the config
private Jackson2JsonObjectMapper jackson2JsonObjectMapper;
#Override
public void commence(HttpServletRequest request,
HttpServletResponse response,
AuthenticationException e) throws IOException {
/*
This is a pojo you can create to hold the repsonse code, error, and description.
You can create a POJO to hold whatever information you want to send back.
*/
CustomError error = new CustomError(HttpStatus.FORBIDDEN, error, description);
/*
Here we're going to creat a json strong from the CustomError object we just created.
We set the media type, encoding, and then get the write from the response object and write
our json string to the response.
*/
try {
String json = jackson2JsonObjectMapper.toJson(error);
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
response.setCharacterEncoding(StandardCharsets.UTF_8.toString());
response.getWriter().write(json);
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
CustomAccessDeniedHandler.java
This handles authorization errors such as trying to access method without the
appropriate priviledges. You can implement it in the same way we did above with the bad credentials exception.
#Component
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
#Override
public void handle(HttpServletRequest request, HttpServletResponse response,
AccessDeniedException e) throws IOException, ServletException {
// You can create your own repsonse here to handle method level access denied reponses..
// Follow similar method to the bad credentials handler above.
}
}
Hopefully this is somewhat helpful.
Let's say I have the following runtime exception:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public class ExpectationsFailedException extends RuntimeException {
public ExpectationsFailedException(String message) {
super(message);
}
}
My question is if it is ok to throw the previous HTTP exception in my service layer or should I throw it from my controller:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password){
if(parameters_are_not_valid){
throw new ExpectationsFailedException("Invalid input");
}
}
}
The controller exception throwing solution would be the following:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password) throws InvalidInputParameters {
if(parameters_are_not_valid){
throw new InvalidInputParameters("Invalid input");
}
}
}
and in my controller
#RestController
public class XController{
#Autowired
private UserService userService;
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public void addUser(#Valid #RequestBody SignUpForm form, BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ExpectationsFailedException("Input parameters conditions were not fulfilled");
}
try {
userService.addUser(...);
}
catch(InvalidInputParameters ex){
throw new ExpectationsFailedException("Invalid service input parameters");
}
}
}
Which of those solutions is preferred? Why? I have a feeling that I should not throw HTTP exceptions in my services because I may use that services in other contexts which may not be related to HTTP.
I would go with the second one.
What do you think?
I agree with your last statement. Your service layer should be independent of HTTP or frontent frameworks (#ResponseStatus is Spring MVC annotation and therefore it's not the best practice to use it in your service layer).
However you don't have to throw one exception in service layer, catch it in controller and rethrow another exception annotated with #ResponseStatus. Just add exception handler for the service exception and return appropriate response status from it. You have plenty of options, for instance #ExceptionHandler:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
#ExceptionHandler(InvalidInputParameters.class)
public void handle() {
// Do nothing, just return the status
}
You can put this code to #ControllerAdvice annotated class to enable it for all controllers or just in you controller if it's not needed elsewhere.