Currently, there is a GetMapping like follows
#GetMapping(value = "/{id}")
public ResponseEntity<Dog> getTrainById(#PathVariable Long id) {
Dog dog= animalService.getAnimalById(id);
return new ResponseEntity<>(Dog , HttpStatus.OK);
}
now if someone accesses http://localhost:8080/api/animal/1, it returns the animal.
But I need to throw NoHandlerFoundException if someone accesses this endpoint without a Long variable as a path parameter, that means like this http://localhost:8080/api/animal/asdsad
IF anyone can tell me way to achieve this, that would be much appreciated
Also I have global exception handling like follows
#ControllerAdvice
public class DemoExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(EntityNotFoundException.class)
public ResponseEntity<GenericResponse> customHandleNotFound(Exception ex, WebRequest request)
{
return new ResponseEntity<>(new GenericResponse(ex.getMessage(), null), HttpStatus.NOT_FOUND);
}
#Override
protected ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex,
HttpHeaders headers, HttpStatus status, WebRequest request) {
return new ResponseEntity<>(new GenericResponse("invalid endpoint", null), HttpStatus.METHOD_NOT_ALLOWED);
}
}
In this case which the request cannot be resolved to the parameter type of a controller method , it will throw MethodArgumentTypeMismatchException.
So the most effective way to solve the problem is to think about how to handle MethodArgumentTypeMismatchException directly but not think how to make it re-throw NoHandlerFoundException. So you can simply create a #ControllerAdvice to handle MethodArgumentTypeMismatchException :
#ControllerAdvice
public class DemoExceptionHandler {
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Object> handle(MethodArgumentTypeMismatchException ex) {
return new ResponseEntity<>( GenericResponse("invalid endpoint", null), HttpStatus.METHOD_NOT_ALLOWED);
}
}
It will be applied to all controllers that throw this type of exception . If you only want it to apply for a particular controller but not globally , you can do :
#RestController
#RequestMapping("/foo")
public class FooController {
#GetMapping(value = "/{id}")
public ResponseEntity<Dog> getTrainById(#PathVariable Long id) {
}
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
public ResponseEntity<Object> handleMethodArgumentTypeMismatchException() {
return new ResponseEntity<>( GenericResponse("invalid endpoint", null), HttpStatus.METHOD_NOT_ALLOWED);
}
}
Related
#Order(Ordered.HIGHEST_PRECEDENCE)
#RestControllerAdvice(basePackages = "com.cvresumeproject.cvresumeproject.service.*")
public class RestExceptionHandler {
#ExceptionHandler(value = {CustomNotFoundException.class})
protected ResponseEntity<Object> handleEntityNotFound(CustomNotFoundException exception){
ApiError apiError =new ApiError(HttpStatus.NOT_FOUND);
apiError.setMessage(exception.getMessage());
return new ResponseEntity<>(apiError,apiError.getStatus());
}
}
Main class
#SpringBootApplication
#ComponentScan("com.cvresumeproject.cvresumeproject.ExceptionHandler.RestExceptionHandler")
public class CvresumeprojectApplication {
public static void main(String[] args) {
SpringApplication.run(CvresumeprojectApplication.class, args);
}
}
Using my customnotfoundexception
#Override
public TemplateDto findById(Long id) throws CustomNotFoundException {
return templateMapper.templateToTemplateDto(templateRepository.findById(id).get());
}
My resume project using exceptionhandler but this exception handler not working please help me Thanks!!!
You don't need * in the basePackages element in #RestControllerAdvice. Controllers that belong to the configured base packages or sub-packages thereof will be included, so you don't need the wildcard. Use the following instead:
#RestControllerAdvice(basePackages = "com.cvresumeproject.cvresumeproject.service")
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {CustomNotFoundException.class})
protected ResponseEntity<Object> handleEntityNotFound(Exception exception, WebRequest webRequest) {
HttpStatus errorCode = HttpStatus.NOT_FOUND;
ApiError apiError = new ApiError(errorCode);
apiError.setMessage(exception.getMessage());
return this.handleExceptionInternal(exception, apiError, new HttpHeaders(), errorCode, webRequest);
}
}
The controller:
#RestController
#RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
#Validated
public class ApiController {
#PostMapping(value = "/in",
consumes = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<InitResponse> inPost(
#ApiParam(required = true) #Valid #RequestBody InRequest inRequest) {
LOG.info("inPost request was received = {}", inRequest);
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
The exception handler:
#ControllerAdvice(assignableTypes = ApiController .class, annotations = RestController.class)
public class InExceptionHandler {
#ExceptionHandler(ConstraintViolationException.class)
public ResponseEntity<INErrors> handleConstraintViolation(ConstraintViolationException ex) {
LOG.info("handleConstraintViolation was trigerred");
INError INError = new INError(HttpStatus.BAD_REQUEST.toString(), ex.getLocalizedMessage());
return new ResponseEntity<>(new INErrors(), HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<INErrors> handleMethodArgumentConstraintViolation(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return new ResponseEntity<>(processFieldErrors(fieldErrors), HttpStatus.BAD_REQUEST);
}
}
If the InRequest has all fields within the javax validation constraint then I get the right code, but when a field doesn't match validation I just get 400 response code.
There are other exception handlers defined but I've put breakpoints everywhere and nothing is triggered.
I also added the log4j property:
log4j.logger.org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod=DEBUG,stdout
but this didn't produce any additional output when debugging.
I'm expecting to also to have the INErrors object sent back, but it doesn't even enter either one of the 2 handling methods.
This is happening because Spring's default exception handler handles all WebMvc's standard exceptions by itself and then delegates unhandled exceptions to user-defined #ExceptionHandler methods.
In your case #Valid constraint violation throws Spring's MethodArgumentNotValidException which is handled by ResponseEntityExceptionHandler#handleMethodArgumentNotValid. So, to change the default behaviour for this exception, you need to override this method in your #ControllerAdivce.
#ControllerAdvice(assignableTypes = ApiController .class, annotations = RestController.class)
public class InExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return ResponseEntity.badRequest().body(processFieldErrors(fieldErrors));
}
}
EDIT: I saw that you're using both assignableTypes and annotations for #ControllerAdvice exception handler. This makes Spring register one exception handler for all #RestControllers. Try using either assignableTypes or annotations.
As an option, you can create your custom annotation for different exception handlers.
Following code prints "one" when invalid data supplied to /one and "two" when data was sent to "/two".
#RestController
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface One {}
#RestController
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#interface Two {}
#One
class ControllerOne {
#PostMapping("one")
String a(#RequestBody #Valid Data data) {
return data.value;
}
}
#Two
class ControllerTwo {
#PostMapping("two")
String a(#RequestBody #Valid Data data) {
return data.value;
}
}
#ControllerAdvice(annotations = One.class)
class HandlerOne extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return ResponseEntity.badRequest().body("one");
}
}
#ControllerAdvice(annotations = Two.class)
class HandlerTwo extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
return ResponseEntity.badRequest().body("two");
}
}
The javax annotation style validation for the model worked for me only when I added the method inside the controller :
public class ApiController {
...
#ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<INErrors> handleConstraintViolation(MethodArgumentNotValidException exception) {
BindingResult result = exception.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return new ResponseEntity<>(processFieldErrors(fieldErrors), HttpStatus.BAD_REQUEST);
}
}
Have a custom error controller on Spring boot:
package com.example.controllers;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.boot.web.servlet.error.ErrorController;
import javax.servlet.http.HttpServletRequest;
#Controller
public class CustomErrorController implements ErrorController
{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
#Override
public String getErrorPath()
{
return "/error";
}
}
But, when compile says: getErrorPath() in ErrorController has been deprecated. Ok, i found information: use server.error.path property. Ok, add this in application.properties and delete the function, but now says: CustomErrorController is not abstract and does not override abstract method getErrorPath() in ErrorController, ¿need a deprecated function?.
How to made the custom error controller?, the ErrorController requires getErrorPath but it is deprecated, what is the correct alternative?.
Starting version 2.3.x, Spring boot has deprecated this method. Just return null as it is anyway going to be ignored. Do not use #Override annotation if you want to prevent future compilation error when the method is totally removed. You can also suppress the deprecation warning if you want, however, the warning (also the #Override annotation) is helpful to remind you to cleanup/fix your code when the method is removed.
#Controller
#RequestMapping("/error")
#SuppressWarnings("deprecation")
public class CustomErrorController implements ErrorController {
public String error() {
// handle error
// ..
}
public String getErrorPath() {
return null;
}
}
#Controller
public class CustomErrorController implements ErrorController {
#RequestMapping("/error")
public ModelAndView handleError(HttpServletResponse response) {
int status = response.getStatus();
if ( status == HttpStatus.NOT_FOUND.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-404");
} else if (status == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
System.out.println("Error with code " + status + " Happened!");
return new ModelAndView("error-500");
}
System.out.println(status);
return new ModelAndView("error");
}
}
there is an #ControllerAdvice annotation
#ControllerAdvice
public class MyErrorController {
#ExceptionHandler(RuntimeException.class)
public String|ResponseEntity|AnyOtherType handler(final RuntimeException e) {
.. do handle ..
}
#ExceptionHandler({ Exception1.class, Exception2.class })
public String multipleHandler(final Exception e) {
}
}
To handle errors, There is no need to define a controller class
implementing an error controller.
To handle errors in your entire application instead of writing
#Controller
public class CustomErrorController implements ErrorController{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
...
}
}
use the below class
#ControllerAdvice
public class myExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(Exception.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClassexceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// Its an example you can define a class with your own structure
return new ResponseEntity<>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(CustomException.class)
public final ResponseEntity<YourResponseClass> handleAllExceptions(Exception ex, WebRequest request) {
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage()); // For reference
return new ResponseEntity<YourResponseClass>(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
}
#ExceptionHandler(BadCredentialsException.class)
public final ResponseEntity<YourResponseClass> handleBadCredentialsException(BadCredentialsException ex, WebRequest request){
YourResponseClass exceptionResponse = new YourResponseClass(new Date(), ex.getMessage());// For refernece
return new ResponseEntity<>(exceptionResponse, HttpStatus.UNAUTHORIZED);
}
}
The class above annoted with #ControllerAdvice acts as custom exception handler and it handles all the expecptions thrown by ur application. In above code sample only three exceptions are showed for understanding. It can handle many exceptions
In your application if there's any exception thrown it will come to this class and send the response back. You can have a customized message and structure as per ur needs.
#Controller
public class AppErrorController implements ErrorController {
#RequestMapping("/error")
public String handleError(HttpServletRequest request) {
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if(status != null) {
int statusCode = Integer.valueOf(status.toString());
if (statusCode == HttpStatus.FORBIDDEN.value()) {
return "errorpages/error-403";
} else if (statusCode == HttpStatus.NOT_FOUND.value()) {
return "errorpages/error-404";
} else if (statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "errorpages/error-500";
}
}
return "errorpages/error";
}
}
given the following dto and controller
public class PasswordCredentials implements AuthenticationProvider {
#NotNull
#NotEmpty
#JsonProperty( access = JsonProperty.Access.WRITE_ONLY )
private String user;
#NotNull
#NotEmpty
#JsonProperty( access = JsonProperty.Access.WRITE_ONLY )
private CharSequence pass;
public void setPass( final CharSequence pass ) {
this.pass = pass;
}
public void setUser( final String user ) {
this.user = user;
}
#Override
public Authentication toAuthentication() {
return new UsernamePasswordAuthenticationToken( user, pass );
}
}
#RestController
#RequestMapping( path = "authentication" )
class AuthenticationController {
private final AuthenticationManager am;
AuthenticationController( final AuthenticationManager am ) {
this.am = am;
}
#RequestMapping( path = "password", method = RequestMethod.POST, consumes = {
"!" + MediaType.APPLICATION_FORM_URLENCODED_VALUE
} )
ResponseEntity<?> login( #Valid #RequestBody final PasswordCredentials credentials ) {
Authentication authenticate = am.authenticate( credentials.toAuthentication() );
if ( authenticate.isAuthenticated() ) {
return ResponseEntity.status( HttpStatus.NO_CONTENT ).build();
}
return ResponseEntity.status( HttpStatus.FORBIDDEN ).build();
}
}
if for example pass is null there will be a validation error, and a 400 will happen without ever calling my controller, which is fine. That 400 however has no content, is there any way to have the controllers BindResults output as content so that the consumer of the API knows what caused the problem? Ideally I would not do this in the controller method, so that it would happen on all controllers?
I was able to get this behavior with spring data rest as follows, but I'd like it for all API controllers.
class RestConfig extends RepositoryRestConfigurerAdapter {
#Bean
Validator validator() {
return new LocalValidatorFactoryBean();
}
#Override
public void configureValidatingRepositoryEventListener(
final ValidatingRepositoryEventListener validatingListener ) {
Validator validator = validator();
//bean validation always before save and create
validatingListener.addValidator( "beforeCreate", validator );
validatingListener.addValidator( "beforeSave", validator );
}
#Override
public void configureRepositoryRestConfiguration( final RepositoryRestConfiguration config ) {
config.setBasePath( "/v0" );
config.setReturnBodyOnCreate( false );
config.setReturnBodyOnUpdate( false );
}
Spring have #ControllerAdvice and #ExceptionHandler annotation to handle errors in controllers.
#ControllerAdvice
public class ExceptionTranslator {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public Error processValidationError(MethodArgumentNotValidException ex) {
BindingResult result = ex.getBindingResult();
.....
return new Error();
}
// Other exceptions
}
i want to improve the answer of Anton Novopashin: just return the error in response entity.
#ControllerAdvice
public class ExceptionTranslator {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseBody
public ResponseEntity<String> processValidationError(MethodArgumentNotValidException ex) {
return new ResponseEntity(ex.getMessage, HttpStatus.BAD_REQUEST);
}
// Other exceptions
}
I'm not sure who or why downvoted the existing answers but they are both right - the best way to handle validation errors would be to declare a #ControllerAdvice and then handle the exceptions there. Here's a snippet of my global error handler, taken from an existing project:
#ControllerAdvice
#ResponseBody
public class RestfulErrorHandler {
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MethodArgumentNotValidException.class)
public ErrorResponse methodValidationError(MethodArgumentNotValidException e) {
final ErrorResponse response = new ErrorResponse();
for (ObjectError error : e.getBindingResult().getAllErrors()) {
if (error instanceof FieldError) {
response.addFieldError((FieldError) error);
} else {
response.addGeneralError(error.getDefaultMessage());
}
}
return response;
}
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(ConstraintViolationException.class)
public ErrorResponse constraintViolationError(ConstraintViolationException e) {
final ErrorResponse response = new ErrorResponse();
for (ConstraintViolation<?> v : e.getConstraintViolations()) {
response.addFieldError(v.getPropertyPath(), v.getMessage());
}
return response;
}
}
You should also process ConstraintViolationExceptions since they too may be thrown. Here's a link to my ErrorResponse class, I'm including it as a Gist so as not to obscure the main point.
You should also probably process the RepositoryConstraintViolationException, I'm not sure if spring-data-rest handles them already.
I have a HandlerInterceptorAdapter that intercepts all requests and performs user authorization checks. Very basically:
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
User user = ... // get user
checkIfAuthorized(user); // throws AuthorizationException
return true;
}
I then have an #ExceptionHandler for that AuthorizationException.
#ExceptionHandler(value = AuthorizationException.class)
public ResponseEntity<String> handleNotAuthorized(AuthorizationException e) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
return responseEntity;
}
This is fine if the (unauthorized) request accepts text/plain (and can be easily changed for json).
How can I make different #ExceptionHandlers for specific Accept headers?
#RequestMapping has produces(). Is there something similar for #ExceptionHandler?
I know this comes late but I've been looking up a solution to this, came across this question and found what I think to be a better solution. You can return "forward:/error" in your #ExceptionHandler (returning a String) to forward the request to a
#RequestMapping("/error")
ErrorController {...}
and use
#RequestMapping(produces = "text/html")
ModelAndView errorPage() {...}
on one method of that ErrorController,
#RequestMapping(produces = "application/json") // or no 'produces' attribute for a default
MyJsonObject errorJson() {...} on another.
I think this is a pretty neat way to do it, it's probably already out there but I didn't find it when trying to look it up.
So basically the #ExceptionHandler is the same for all, but forwards to a controller that can do the usual stuff
I think of two approaches:
Manually
public ResponseEntity<String> handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
if (/*read header accept from request and build appropiate response*/) {}
ResponseEntity<String> responseEntity = new ResponseEntity<>("You are not authorized to access that page.", HttpStatus.UNAUTHORIZED);
return responseEntity;
Automatically
#ResponseBody
public SomeObject handleNotAuthorized(AuthorizationException e, HttpServletRequest request) {
// TODO Custom EXCEPTION HANDLER for json/jsp/xml/other types, based on content type
/* Construct someObject and let Spring MessageConverters transform it to JSON or XML. I don't remember what happens in case of HTML (it should go to a view)*/
return someObject;
Don't forget to set the Response's Status code.
Not exactly the same use case, but the same requirement. I solve it with a custom HttpMessageConverter implementation.
#RestController
#RequestMapping("/foo")
public class MyResource {
#GetMapping(path = "/{id}", produces = "application/json")
public ResponseEntity<MyDto> get (#PathVariable(ID) long id)
throws IOException {
throw new MyCustomException();
}
#GetMapping(path = "/{id}/export", produces = "application/zip")
public ResponseEntity<byte[]> export (#PathVariable(ID) long id)
throws IOException {
throw new MyCustomException();
}
}
...
#ControllerAdvice
public class MyCustomExceptionHandler {
#ResponseBody
#ExceptionHandler
#ResponseStatus(BAD_REQUEST)
public JsonAPIErrorDocument handleException (MyCustomException e) {
return ....;
}
}
...
public class JsonAPIErrorDocumentToByteArrayMessageConverter extends AbstractHttpMessageConverter {
public ErrorDocumentToByteArrayMessageConverter () {
super(new MediaType("application", "zip"), MediaType.ALL);
}
#Override
protected boolean supports (Class clazz) {
return JsonAPIErrorDocument.class == clazz;
}
#Override
protected Object readInternal (Class clazz, HttpInputMessage inputMessage)
throws IOException,
HttpMessageNotReadableException {
return new byte[0];
}
#Override
protected void writeInternal (Object t, HttpOutputMessage outputMessage)
throws IOException,
HttpMessageNotWritableException {
}
}
...
#EnableWebMvc
#Configuration
#ComponentScan({ "com.foo" })
public class ApplicationConfig implements WebMvcConfigurer {
...
#Override
public void configureMessageConverters (List<HttpMessageConverter<?>> converters) {
converters.add(new MappingJackson2HttpMessageConverter(objectMapper));
converters.add(new ByteArrayHttpMessageConverter());
converters.add(new JsonAPIErrorDocumentToByteArrayMessageConverter());
}
...
}