I'm trying to handle 404 error using an #ControllerAdvice in a Spring MVC application totally configured using Java configuration.
Here you have my conf:
public class WebAppInitializer implements WebApplicationInitializer
{
#Override
public void onStartup(ServletContext container)
{
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherServlet = new AnnotationConfigWebApplicationContext();
dispatcherServlet.register(WebMvcConfig.class);
dispatcherServlet.setServletContext(container);
dispatcherServlet.refresh();
CookieHelper cookie = (CookieHelper) dispatcherServlet.getBean("cookie");
final Gson gson = (Gson) dispatcherServlet.getBean("gson");
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(dispatcherServlet));
dispatcher.addMapping("/");
dispatcher.setLoadOnStartup(1);
dispatcher.setInitParameter("throwExceptionIfNoHandlerFound", "true");
FilterRegistration.Dynamic filter = container.addFilter("BaseFilter", new BaseFilter(cookie, gson));
filter.setInitParameter("forceEncoding", "true");
filter.addMappingForUrlPatterns(null, true, "/coolers/*");
filter.addMappingForUrlPatterns(null, true, "/hothouses/*");
filter.addMappingForUrlPatterns(null, true, "/lang/*");
filter.addMappingForUrlPatterns(null, true, "/organizations/*");
filter.addMappingForUrlPatterns(null, true, "/reworks/*");
filter.addMappingForUrlPatterns(null, true, "/select/*");
filter.addMappingForUrlPatterns(null, true, "/volumes/*");
}
}
and my GlobalExceptionHandlerController:
#ControllerAdvice
public class GlobalExceptionHandlerController
{
#ExceptionHandler(NoHandlerFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handle() {
System.out.println("test test test test");
return "error/index";
}
}
NoHandlerFoundException not firing?
I had the same issue, got it resolved. Below given steps to solve the same.
Create a class GlobalExceptionHandler annotated with #ControllerAdvice
#ControllerAdvice
public class GlobalExceptionHandler
{
#ExceptionHandler(NoHandlerFoundException.class)
public String handleNotFoundError(Exception ex)
{
return "redirect:/yourCustom404page";
}
}
By default, when a page/resource does not exist the servlet container will render a default 404 page. If you want a custom 404 response then you need to tell DispatcherServlet to throw the exception if no handler is found. We can do this by setting the throwExceptionIfNoHandlerFound servlet initialization parameter to true
a. If spring-mvc java based configuration is
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer
{
...
#Override
protected DispatcherServlet createDispatcherServlet(WebApplicationContext servletAppContext)
{
final DispatcherServlet servlet = (DispatcherServlet) super.createDispatcherServlet(servletAppContext);
servlet.setThrowExceptionIfNoHandlerFound(true);
return servlet;
}
}
b. if spring-mvc xml based configuration, initialize your dispatcher servlet like this
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
c. if spring-boot
spring.resources.add-mappings=false in your application.properties or yaml file.
Hope it helps
Related
I am running a Spring 4 web mvc project:
Issue:
My controlleradvice for 404 exception handler is not working. However, if I comment the "addResourceHandlers" method in WebConfig class, it will work. (I can't remove that as it resolves my static resources)_
This is my web config:
#EnableWebMvc
#Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
/*
* Resource handler for static resources
*/
#Override
public void addResourceHandlers(final ResourceHandlerRegistry registry) {
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
}
And this is my 404 exception handler:
#ControllerAdvice
public class ExceptionController {
#ExceptionHandler(NoHandlerFoundException.class)
public String handle404(Exception e) {
return "error/404";
}
}
If your webapp is using web.xml it's very simple - just add the following (assuming usage of InternalResourceViewResolver with prefix pointing at your WEB-INF view folder and suffix .jsp). You can have multiple error-page elements of other error codes too.
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
If you are not using web.xml it's a bit more complicated.
If you want to catch the NoHandlerFound exception you first have to tell Spring to throw it via setting a flag in the DispatcherServlet directly.
To do so, in the class that you are extending AbstractAnnotationConfigDispatcherServletInitializer override the onStartup method to expose the DispatcherServlet definition and add manually the needed flag:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
//...
WebApplicationContext context = getContext();
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
//we did all this to set the below flag
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",dispatcherServlet );
//..
}
Then your existing code within ExceptionController should work and intercept the exception
I have simple Spring MVC application where I want to hande 404 Not found exceptions in my Advice Controller class
Configuration:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
protected Class<?>[] getServletConfigClasses() {
return new Class<?>[]{WebConfig.class};
}
#Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?>[]{RootConfig.class, SecurityConfig.class};
}
#Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new SessionListener());
FilterRegistration.Dynamic encodingFilter = servletContext.addFilter("encodingFilter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForUrlPatterns(null, true, "/*");
}
}
Controller:
#Controller
#RequestMapping("/")
public class HomeController {
#RequestMapping(value = "/error/", method = RequestMethod.GET)
public String error(){return "error";}
}
ControllerAdvice:
#ControllerAdvice
public class AdviceController {
#ExceptionHandler(MyOwnException.class)
#ResponseStatus(value= HttpStatus.BAD_REQUEST)
public String checkoutException(CheckoutException e, HttpServletRequest httpServletRequest) {
return "error";
}
}
I can catch my own exceptions when I manually throw MyOwnException but I can't get how to catch NoHandlerFound exception. I need to send 404 error code and appropriate error.jsp page when there is no controller method to handle request
If your webapp is using web.xml it's very simple - just add the following (assuming usage of InternalResourceViewResolver with prefix pointing at your WEB-INF view folder and suffix .jsp). You can have multiple error-page elements of other error codes too.
<error-page>
<error-code>404</error-code>
<location>/error</location>
</error-page>
If you are not using web.xml it's more complicated and you'll have to define and register your own ExceptionResolver. Take a look at this spring.io blog article for details on how to do this.
(Edit after comment)
If you want to catch the NoHandlerFound exception you first have to tell Spring to throw it via setting a flag in the DispatcherServlet directly. To do so, in your AppInitializer class add the DispatcherServlet definition on top of what you are currently doing to add the flag:
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
super.onStartup(servletContext);
servletContext.addListener(new SessionListener());
//BEGIN OF NEW CODE
WebApplicationContext context = getContext();
DispatcherServlet dispatcherServlet = new DispatcherServlet(context);
//we did all this to set the below flag
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("DispatcherServlet",dispatcherServlet );
//END OF NEW CODE
FilterRegistration.Dynamic encodingFilter = servletContext.addFilter("encodingFilter", new CharacterEncodingFilter());
encodingFilter.setInitParameter("encoding", "UTF-8");
encodingFilter.setInitParameter("forceEncoding", "true");
encodingFilter.addMappingForUrlPatterns(null, true, "/*");
}
Then you can catch the NoHandlerFound exception directly in your AdviceController:
#ControllerAdvice
public class AdviceController {
//..
#ExceptionHandler(NoHandlerFoundException.class)
public String dealWithNoHandlerFoundException(CheckoutException e, HttpServletRequest httpServletRequest) {
return "error";
}
}
I would like to display custom 404 error page if a user enters a request path that isn't handled by any controller.
Suppose that I have only one controller and it handles the following request paths: path1, path2 and path3. How can I display a custom 404 error page if a user enters path4? Currently, Tomcat displays its own 404 error page. I would like to use annotations and not implement this task by configuring any xml files.
My current approach doesn't achieve this requirement.It might be possible to configure any filters, but I am not aware of such approach. Thus, I will appreciate any ideas about it.
Here is my code:
public class WebAppContextInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext annotationConfigWebApplicationContext = new AnnotationConfigWebApplicationContext();
annotationConfigWebApplicationContext.register(WebContextConfiguration.class);
DispatcherServlet dispatcherServlet = new DispatcherServlet(annotationConfigWebApplicationContext);
dispatcherServlet.setThrowExceptionIfNoHandlerFound(true);
ServletRegistration.Dynamic dispatcher = servletContext.addServlet("SpringDispatcher",dispatcherServlet);
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
servletContext.setInitParameter("spring.profiles.active","demo");
}
}
#ControllerAdvice
class GlobalControllerExceptionHandler {
#ExceptionHandler(NoHandlerFoundException.class)
public String handleConflict() {
return "404_error_page";
}
}
I'm trying to add a CORS filter to my Spring web application but the filter is not being executed. I've followed the relevant steps here: https://spring.io/guides/gs/rest-service-cors/ to no avail. I'm not using Spring Boot. I'm bootstrapping my application using Spring's WebApplicationInitializer in a 3.0+ servlet spec container.
Everything else in my application is working: configuration classes, controllers, hibernate entities, etc.
Update 1:
The below answers worked for me by adding the filter to the servlet container:
container.addFilter("CorsFilter", CorsFilter.class)
.addMappingForUrlPatterns(null, false, "/*");
However, I'm curious why Spring Boot does not require this? They must automatically search for Filters and add them to the context. Is there a straightforward way to do this? It seems unfortunate that I have to create a filter and then add it to some list in another class. It'd be nice if they were automatically registered like in Spring Boot.
Relevant code snippets:
WebApplicationInitializer:
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebAppConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
}
}
AppConfig:
#Configuration
#ComponentScan
public class AppConfig {
}
WebAppConfig:
#Configuration
#EnableWebMvc
#EnableSpringDataWebSupport
#ComponentScan(basePackageClasses = AppConfig.class)
public class WebAppConfig extends WebMvcConfigurerAdapter {
}
CorsFilter:
#Component
public class CorsFilter implements Filter {
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setHeader("Access-Control-Allow-Credentials", "true");
httpResponse.setHeader("Access-Control-Allow-Methods", "POST, PUT, GET, OPTIONS, DELETE");
httpResponse.setHeader("Access-Control-Max-Age", "3600");
httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with, Content-Type");
HttpServletRequest httpRequest = (HttpServletRequest) request;
String origin = httpRequest.getHeader("Origin");
if (StringUtils.hasText(origin)) {
boolean isMyDomain =
Pattern.compile("^https://(.*?\\.)?mydomain.com(:\\d+)?$")
.matcher(origin)
.find();
if (isMyDomain) {
httpResponse.setHeader("Access-Control-Allow-Origin", origin);
} else {
httpResponse.setHeader("Access-Control-Allow-Origin", "mydomain");
}
}
chain.doFilter(request, response);
}
#Override
public void init(FilterConfig filterConfig) throws ServletException {
LoggerFactory.getLogger(getClass()).info("CorsFilter initiated");
}
#Override
public void destroy() {
LoggerFactory.getLogger(getClass()).info("CorsFilter destroyed");
}
}
You could add like this:
container.addFilter("CorsFilter", CorsFilter.class)
.addMappingForUrlPatterns(null, false, "/*");
You have to just register your Filter in your AppInitializer.
public class AppInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
// Create the 'root' Spring application context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(AppConfig.class);
// Manage the lifecycle of the root application context
container.addListener(new ContextLoaderListener(rootContext));
// Create the dispatcher servlet's Spring application context
AnnotationConfigWebApplicationContext dispatcherContext =
new AnnotationConfigWebApplicationContext();
dispatcherContext.register(WebAppConfig.class);
// Register and map the dispatcher servlet
ServletRegistration.Dynamic dispatcher =
container.addServlet("dispatcher", new DispatcherServlet(dispatcherContext));
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/");
//Added filter dynamically
javax.servlet.FilterRegistration.Dynamic corsFilter = container.addFilter("corsfilter", CORSFilter.class);
corsFilter.addMappingForUrlPatterns(null, true, "/*");
}
}
You can take reference of this github repository.
I use #ExceptionHandler to handle exceptions thrown by my web app, in my case my app returns JSON response with HTTP status for error responses to the client.
However, I am trying to figure out how to handle error 404 to return a similar JSON response like with the one handled by #ExceptionHandler
Update:
I mean, when a URL that does not exist is accessed
I use spring 4.0 and java configuration. My working code is:
#ControllerAdvice
public class MyExceptionController {
#ExceptionHandler(NoHandlerFoundException.class)
public ModelAndView handleError404(HttpServletRequest request, Exception e) {
ModelAndView mav = new ModelAndView("/404");
mav.addObject("exception", e);
//mav.addObject("errorcode", "404");
return mav;
}
}
In JSP:
<div class="http-error-container">
<h1>HTTP Status 404 - Page Not Found</h1>
<p class="message-text">The page you requested is not available. You might try returning to the home page.</p>
</div>
For Init param config:
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
#Override
public void customizeRegistration(ServletRegistration.Dynamic registration) {
registration.setInitParameter("throwExceptionIfNoHandlerFound", "true");
}
}
Or via xml:
<servlet>
<servlet-name>rest-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>throwExceptionIfNoHandlerFound</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
See Also: Spring MVC Spring Security and Error Handling
With spring > 3.0 use #ResponseStatus
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
...
}
#Controller
public class MyController {
#RequestMapping.....
public void handleCall() {
if (isFound()) {
// do some stuff
}
else {
throw new ResourceNotFoundException();
}
}
}
Simplest way to find out is use the following:
#ExceptionHandler(Throwable.class)
public String handleAnyException(Throwable ex, HttpServletRequest request) {
return ClassUtils.getShortName(ex.getClass());
}
If the URL is within the scope of DispatcherServlet then any 404 caused by mistyping or anything else will be caught by this method but if the URL typed is beyond the URL mapping of the DispatcherServlet then you have to either use:
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>
or
Provide "/" mapping to your DispatcherServlet mapping URL so as to handle all the mappings for the particular server instance.
public final class ResourceNotFoundException extends RuntimeException {
}
#ControllerAdvice
public class AppExceptionHandler {
#ExceptionHandler(ResourceNotFoundException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public String handleNotFound() {
return "404";
}
}
Just define an Exception, an ExceptionHandler, throw the Exception from your business code controller.
You can use servlet standard way to handle 404 error. Add following code in web.xml
<error-page>
<exception-type>404</exception-type>
<location>/404error.html</location>
</error-page>