I created my own exception and trying to handle it with spring MVC.
Spring Handling all exceptions as it should except my own exceptions(User Defined). What is the problem, how to handle User Defined Exceptions in Spring MVC?
I used #ExceptionHandler in my controller and in #ControllerAdvice, used SimpleMappingExceptionResolver bean in Dispatcher servlet no help. In every stage all the System defined exceptions are handled as expected.
User Defined Exception:
public class GeneralUserException extends Exception {
private static final long serialVersionUID = 1L;
String message;
public GeneralUserException (String message){
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
Exception Handler in controller:
#ExceptionHandler(value = GeneralUserException.class)
public String defaultErrorHandler(HttpServletRequest req, GeneralUserException e) {
return "general_error";
}
Exception Handler in #ControllerAdvice:
#ControllerAdvice
class GlobalDefaultExceptionHandler {
#ExceptionHandler(value = GeneralUserException.class)
public String defaultErrorHandler(HttpServletRequest req, GeneralUserException e) {
return "general_error";
}
}
Bean in dispatcher Servlet Context (WebAppConfig):
public class WebAppConfig extends WebMvcConfigurerAdapter{
#Bean
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
Properties mappings = new Properties();
mappings.setProperty("GeneralUserException", "general_error");
r.setExceptionMappings(mappings);
r.setDefaultErrorView("error");
return r;
}
...
}
All these are not showing error page. But when I change GeneralUserException to Exception/RuntimeException it works. I tried to put full qualified name too.
I use spring, spring MVC, spring Security if it's helpful. And I don't use Spring Aspects.
Note: SimpleMappingExceptionResolver is returning "error" page on error(so its working) for DefaultErrorView. When I use RuntimeException instead GeneralUserException it returns "general_error" view so its working for RuntimeException.
Related
I want Spring to create 2 instances of FooController. Requests to /foo should be handled by one of the instances and requests to /bar should be handled by the other instance. I want something like the below, but of course #RequestMapping doesn't work that way and also Spring gives me the ambiguous mapping error on FooController as well.
#RestController
public class FooController {
String name;
public FooController(String name) { this.name = name; }
}
#Configuration
public class FooControllerConfig {
#Bean
#RequestMapping("/foo")
public FooController getFooFooController(){
return new FooController("foo");
}
#Bean
#RequestMapping("/bar")
public FooController getBarFooController(){
return new FooController("bar");
}
}
I'm really confused by why you need this requirement? Can you please explain why this is required? Is it that each mapping requires a different name?
First you do not map Beans to a RequestMapping. While I am not even sure the spring application would start it would potentially create a new Bean with an identical name every time you access one of these mappings which would probably throw an error.
You could potentially overcome the duplicate names with your own annotation processing but that is way more work then this looks like it is worth.
Just looking at what you have there is there any reason why the following will not meet your requirements?
#RestController
public class FooController {
private static final fooName = "fooName";
private static final barName = "barName";
#RequestMapping("/foo")
public String getFoo(){
return fooName;
}
#RequestMapping("/bar")
public String getBar(){
return barName;
}
}
Don't try this at home. This code was performed by a bored, trained professional...
You can have multiple instances of the same controller class, each of which handles a different URL through the same or a different method in the controller. The only thing is, I don't know how to do it with just annotations. The way I just did it was to dynamically register each request mapping at initialization time. The FooController becomes a prototype bean (defined with annotations) so you can have Spring instantiate it multiple times, once for each mapping
FooController.java
#Controller
#Scope("prototype")
public class FooController {
private String name;
public FooController() {}
public FooController(String name) {
this.name = name;
}
public ResponseEntity<String> handleRequests() throws Exception {
return new ResponseEntity<>("Yo: " + name + " " + this.hashCode(), HttpStatus.OK);
}
EndpointService.java
#Service
public class EndpointService {
#Autowired
private BeanFactory beanFactory;
#Autowired
private RequestMappingHandlerMapping requestMappingHandlerMapping;
public void addFooController(String urlPath, String name) throws NoSuchMethodException {
RequestMappingInfo requestMappingInfo = RequestMappingInfo
.paths(urlPath)
.methods(RequestMethod.GET)
.produces(MediaType.APPLICATION_JSON_VALUE)
.build();
requestMappingHandlerMapping.registerMapping(requestMappingInfo,
beanFactory.getBean(FooController.class, name),
FooController.class.getDeclaredMethod("handleRequests"));
}
#EventListener
public void handleContextRefreshEvent(ContextRefreshedEvent ctxStartEvt) {
try {
addFooController("/blah1", "blahblah1");
addFooController("/blah2", "blahblah2");
addFooController("/blah3", "blahblah3");
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
}
}
Results:
http://localhost:8080/blah1 returns: Yo: blahblah1 1391627345
http://localhost:8080/blah3 returns: Yo: blahblah3 2078995154
This question already has answers here:
Spring 3.2 #ControllerAdvice Not Working
(7 answers)
Closed 3 years ago.
I can't get this exception handling work. I look similar questions/answers but none of them worked. I have this class with #ControllerAdvice to handle application level error handling. This is not a REST application. It's Spring boot & Thymeleaf
#EnableWebMvc
#ControllerAdvice
public class ErrorController {
#ExceptionHandler(LoginFailureException.class)
public ModelAndView handleLoginFailureException(LoginFailureException exception) {
log.info("Handle LoginFailureException");
return getModelAndView(exception.getMessage(), "user/login");
}
// other exception handling methods
private ModelAndView getModelAndView(String message, String viewName) {
ModelAndView mav = new ModelAndView();
FlashMessage flashMessage = new FlashMessage();
flashMessage.setType("danger");
flashMessage.setMessage(message);
mav.getModel().put("flashMessage", flashMessage);
mav.setViewName(viewName);
return mav;
}
}
In my WebSecurityConfigurerAdapter extended class, I have this
.failureHandler(loginFailureHandler)
and LoginFailureHandler class looks like this:
#Component
public class LoginFailureHandler implements AuthenticationFailureHandler {
private MessageSource messageSource;
#Autowired
public LoginFailureHandler(MessageSource messageSource) {
this.messageSource = messageSource;
}
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response,
AuthenticationException exception) throws IOException, ServletException {
throw new LoginFailureException(
messageSource.getMessage("email.password.not.match", null, Locale.getDefault()));
}
}
and when I try to login with a non valid username, my app crashes and throws this exception and never reach to ExceptionHandler method.
com.company.application.controller.exception.LoginFailureException: Email or Username is not valid
at com.company.application.security.LoginFailureHandler.onAuthenticationFailure(LoginFailureHandler.java:34) ~[classes/:na]
#ControllerAdvice will only be hit when exceptions are thrown from #Controller, not #Component.
Since you are 'decorating' your login page with the danger/flash message, you might have to forward the request onto a new 'page' (e.g. user/loginerror) instead?
Try adding this to your main spring boot app to turn off auto config of Error Mvc
Configuration
#SpringBootApplication
#EnableAutoConfiguration(exclude = {ErrorMvcAutoConfiguration.class})
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
In Spring it's easy to autowire beans and have them available anywhere in the app context. Beans can be specialized to a scope such as session/request/web socket etc.
I have a rather unique scenario. I receive a message from a message broker which means the request is not received in a "Controller". Because of this, Spring is not creating #RequestScope beans (All of this logic in Spring is based on using the #Controller/#RequestMapping annotations / DispatchServlet handler).
Is there a way to create a bean within the request scope with the Spring AutowireCapableBeanFactory or some other way?
I want to do something like the below in which the SomeService.handle will be able to access the getName() method of the RequestScopeBean. Currently it throws this exception.
Exception:
BeanCreationException: Error creating bean with name '
scopedTarget.getRequestUtils': Scope 'request' is not active for the
current thread; consider defining a scoped proxy for this bean
Code
#Service
public class MyMessagingReceiver implements SomeMessageReceiver {
private final SomeService someService;
#Autowired
public MyMessagingReceiver(final SomeService someService) {
this.someService = someService;
}
public void onMessage(MessageObject messageObject) {
//possible here to use AutowireCapableBeanFactory in inject the RequestScopeBean bean?
someService.handle(messageObject);
}
}
#Service
public class SomeService {
private final RequestScopeBean requestScopeBean;
#Autowired
public SomeService(RequestScopeBean requestScopeBean) {
this.requestScopeBean = requestScopeBean;
}
public void handle(MessageObject messageObject) {
System.out.println(this.requestScopeBean.getName());
}
}
#Configuration
public class BeanDeclarations {
#Bean
#RequestScope
public RequestScopeBean requestScopeBean() {
return new RequestScopeBean();
}
}
public RequestScopeBean {
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
public class Interceptor extends HandlerInterceptorAdapter {
private RequestScopeBean requestScopeBean;
#Autowired
public Interceptor(RequestScopeBean requestScopeBean) {
this.requestScopeBean = requestScopeBean;
}
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String name = request.getHeader("name");
this.requestScopeBean.setName(name);
}
}
I'm trying to figure out the simplest way to take control over the 404 Not Found handler of a basic Spring Boot RESTful service such as the example provided by Spring:
https://spring.io/guides/gs/rest-service/
Rather than have it return the default Json output:
{
"timestamp":1432047177086,
"status":404,
"error":"Not Found",
"exception":"org.springframework.web.servlet.NoHandlerFoundException",
"message":"No handler found for GET /aaa, ..."
}
I'd like to provide my own Json output.
By taking control of the DispatcherServlet and using DispatcherServlet#setThrowExceptionIfNoHandlerFound(true), I was able to make it throw an exception in case of a 404 but I can't handle that exception through a #ExceptionHandler, like I would for a MissingServletRequestParameterException. Any idea why?
Or is there a better approach than having a NoHandlerFoundException thrown and handled?
It works perfectly Fine.
When you are using SpringBoot, it does not handle (404 Not Found) explicitly; it uses WebMvc error response. If your Spring Boot should handle that exception, then you should do some hack around Spring Boot. For 404, the exception class is NoHandlerFoundException; if you want to handle that exception in your #RestControllerAdvice class, you must add #EnableWebMvc annotation in your Application class and set setThrowExceptionIfNoHandlerFound(true); in DispatcherServlet. Please refer to the following code:
#SpringBootApplication
#EnableWebMvc
public class Application {
#Autowired
private DispatcherServlet servlet;
public static void main(String[] args) throws FileNotFoundException, IOException {
SpringApplication.run(Application.class, args);
}
#Bean
public CommandLineRunner getCommandLineRunner(ApplicationContext context) {
servlet.setThrowExceptionIfNoHandlerFound(true);
return args -> {};
}
}
After this you can handle NoHandlerException in your #RestControllerAdvice class
#RestControllerAdvice
public class AppException {
#ExceptionHandler(value={NoHandlerFoundException.class})
#ResponseStatus(code=HttpStatus.BAD_REQUEST)
public ApiError badRequest(Exception e, HttpServletRequest request, HttpServletResponse response) {
e.printStackTrace();
return new ApiError(400, HttpStatus.BAD_REQUEST.getReasonPhrase());
}
}
I have created ApiError class to return customized error response
public class ApiError {
private int code;
private String message;
public ApiError(int code, String message) {
this.code = code;
this.message = message;
}
public ApiError() {
}
//getter & setter methods...
}
According to the Spring documentation appendix A. there is a boolean property called spring.mvc.throw-exception-if-no-handler-found which can be used to enable throwing NoHandlerFoundException. Then you can create exception handler like any other.
#RestControllerAdvice
class MyExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(MyExceptionHandler.class);
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(NoHandlerFoundException.class)
public String handleNoHandlerFoundException(NoHandlerFoundException ex) {
log.error("404 situation detected.",ex);
return "Specified path not found on this server";
}
}
#ExceptionHandler itself without #ControllerAdvice (or #RestControllerAdvice) can't be used, because it's bound to its controller only.
In short, the NoHandlerFoundException is thrown from the Container, not from your application within your container. Therefore your Container has no way of knowing about your #ExceptionHandler as that is a Spring feature, not anything from the container.
What you want is a HandlerExceptionResolver. I had the very same issue as you, have a look at my solution over there: How to intercept "global" 404s on embedded Tomcats in spring-boot
The #EnableWebMvc based solution can work, but it might break Spring boot auto configurations.
The solution I am using is to implement ErrorController:
#RestController
#RequiredArgsConstructor
public class MyErrorController implements ErrorController {
private static final String ERROR_PATH = "/error";
#NonNull
private final ErrorAttributes errorAttributes;
#RequestMapping(value = ERROR_PATH)
Map<String, Object> handleError(WebRequest request) {
return errorAttributes.getErrorAttributes(request, false);
}
#Override
public String getErrorPath() {
return ERROR_PATH;
}
}
The solution for this problem is:
Configure DispatcherServlet to throw and exception if it doesn't find any handlers.
Provide your implementation for the exception that will be thrown from DispatcherServlet, for this case is the NoHandlerFoundException.
Thus, in order to configure DispatcherServlet you may use properties file or Java code.
Example for properties.yaml,
spring:
mvc:
throw-exception-if-no-handler-found: true
Example for properties.properties,
spring.mvn.throw-exception-if-no-handler-found=true
Example for Java code, we just want to run the command servlet.setThrowExceptionIfNoHandlerFound(true); on startup, I use the InitializingBean interface, you may use another way. I found a very well written guide to run logic on startup in spring from baeldung.
#Component
public class WebConfig implements InitializingBean {
#Autowired
private DispatcherServlet servlet;
#Override
public void afterPropertiesSet() throws Exception {
servlet.setThrowExceptionIfNoHandlerFound(true);
}
}
Be careful! Adding #EnableWebMvc disables autoconfiguration in Spring Boot 2, meaning that if you use the annotation #EnableWebMvc then you should use the Java code example, because the spring.mvc.* properties will not have any effect.
After configuring the DispatcherServlet, you should override the ResponseEntityExceptionHandler which is called when an Exception is thrown. We want to override the action when the NoHandlerFoundException is thrown, like the following example.
#ControllerAdvice
public class MyApiExceptionHandler extends ResponseEntityExceptionHandler {
#Override
public ResponseEntity<Object> handleNoHandlerFoundException(NoHandlerFoundException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
String responseBody = "{\"errormessage\":\"WHATEVER YOU LIKE\"}";
headers.add("Content-Type", "application/json;charset=utf-8");
return handleExceptionInternal(ex, responseBody, headers, HttpStatus.NOT_FOUND, request);
}
}
Finally, adding a break point to method handleException of ResponseEntityExceptionHandler might be helpful for debugging.
Spring Boot with Spring Data Rest - how to use a custom error handler.
Created an error controller I tried to skip the default error handler by using following code.
Why it is not working!
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration(exclude = { BasicErrorController.class })
#EnableMetrics
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
.....................
.....................
and error controller as below
#Component
#RestController
#RequestMapping(value = "/error")
public class CustomErrorController extends BasicErrorController {
public CustomErrorController(ErrorAttributes errorAttributes) {
super(errorAttributes);
// TODO Auto-generated constructor stub
}
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "Error handling";
}
#Override
public String getErrorPath() {
return PATH;
}
}
I haven't used this kind of solution, but, it seems that your request mapping is not right.
The request mapping of CustomErrorController is '/error', and in
#RequestMapping(value = PATH)
public String error() {
return "Error handling";
}
There is a another '/error' in request mapping path. Then the url for this error handler is '/error/error'.
You have #RequestMapping("/error") annotation on your controller and second #RequestMapping("/error") on your method. This results in /error/error mapping, not the /error mapping as you specified in getErrorPath() method and maybe in your configuration (application.properties, server.path.error).