Spring MVC how are exceptions handled when no request mapping? - spring

What happens when all of the request mappings in the controller do not match the incoming request? I was using the exceptionhandler catching Exception.class but that did not seem to catch it? Can anyone explain?

According to documentation #ExceptionHandler deal with unexpected exceptions that occur during controller execution. In the case when incoming request do not match any request mapping, controller method even will not be executed.
Spring-MVC based on Servlet technology. All incoming requests will be processed by DispatcherServlet (that registered in web.xml). DispatcherServlet looks for handlers which can process current request. If no appropriate handler will be found (i.e. no appropriate mapped controller's method will be found), DispatcherServlet initiates 404 error.
When an error occurs, the web container(Tomcat or any other servlet container) generates a default page containing exception message. But you can also specify that the container should return a specific error page for a given exception. For process this error manually you should add into web.xml new error-page element, that will allow you map error code to url:
...
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<error-page>
<error-code>404</error-code>
<location>/page-not-found</location>
</error-page>
...
Because location will be processed by Spring-MVC as usual request you can create controller to handle this request:
#Controller
public class ExceptionController{
#RequestMapping("/page-not-found")
public ModelAndView pageNotFound(){
return new ModelAndView("page-not-found");
}
}
Now just create view with name page-not-found.jsp and show exception info as you wish.

Related

spring-mvc : issue in accessing url pattern configured in web.xml

I have tested like default, extension, path(start with '/' and end with '/*') these three are working fine but exact match url pattern '/test' showing 404 error.Please refer this is my exact code in web.xml file for configuring dispatcher servlet.
<servlet>
<servlet-name>frontController</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>frontController</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
For this url on address bar :http://localhost:8086/MVCFirstApp/test/sugar
I am getting error - 404 page not found for exact match url pattern only.
Please help me out from this, thanks in advance.
Please check few steps before reading the solutions
+ do you have proper controller to handle the requested request(url pattern)
+ do you have proper view returned by the controller
+ and is the base package scanned for controller (annotations)
It would be more convenient if you share the controller code too. But with what I understand with your statement you can try this
add URL patters as
/test/*
Let me explain what this does it will send all the request which comes tru URL /test/ to dispatcher servlet . For example /test/sugar or /test/abc/def/ghi anything, in more simple words any request with URL /test/* is sent to dispatcher servlet which matches and return the proper controller with help of handler mapping .
And make sure you have added the mapping for /test/sugar or /test with proper view in controller . Or if you want sugar to be your value then use #Pathvariable in your controller.

org.omnifaces.EXCEPTION_TYPES_TO_UNWRAP doesn't work for non-ajax requests

I use FullAjaxExceptionHandlerFactory to handle exceptions during both ajax and non-ajax requests.
The OmniFaces showcase page says
The FullAjaxExceptionHandler will transparently handle exceptions during ajax requests exactly the same way as exceptions during synchronous (non-ajax) requests.
I noticed that if an exception happened during a synchronous request, findExceptionRootCause (the method that actually does unwrapping) doesn't get called. Therefore, the rules defined in the web.xml aren't being applied since they rely on FullAjaxExceptionHandler unwrapping.
Does it mean I need to extend the FullAjaxExceptionHandler or is there something I am missing?
Stack trace :
(non-ajax request)
javax.servlet.ServletException:
Caused by: javax.faces.view.facelets.TagAttributeException
Caused by: javax.el.ELException
Caused by: xxx.MyException
web.xml:
<context-param>
<param-name>org.omnifaces.EXCEPTION_TYPES_TO_UNWRAP</param-name>
<param-value>javax.servlet.ServletException,javax.faces.view.facelets.TagAttributeException,javax.el.ELException</param-value>
</context-param>
...
<error-page>
<exception-type>xxx.MyException</exception-type>
<location>/xxx/page-not-found.xhtml</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/xxx/internal-server-error.xhtml</location>
</error-page>
*I remember ELException may be omitted since it will be included anyway. For the sake of certainty, I included every type preceded my exception in the stack trace.
Result:
/xxx/internal-server-error.xhtml is shown
Expected:
/xxx/page-not-found.xhtml is shown
Update:
For the exception mentioned above, if I define <error-page> like
<error-page>
<exception-type>javax.faces.view.facelets.TagAttributeException</exception-type>
<location>/blueglue/templates/error/page-not-found.xhtml</location>
</error-page>
or
<error-page>
<exception-type>javax.servlet.ServletException</exception-type>
<location>/blueglue/templates/error/page-not-found.xhtml</location>
</error-page>
I will get the expected outcome - the 404 page.
For javax.el.ELException and xxx.MyException, the exception results in the 500 page which isn't what I am expecting.
I use FullAjaxExceptionHandler to handle exceptions during both ajax and non-ajax requests.
The FullAjaxExceptionHandler does not handle exceptions during non-ajax requests. Its sole purpose is to handle exceptions during ajax requests the same way as non-ajax requests (namely, showing an error page defined in web.xml).
Further in the showcase page which you linked you can find this section:
Normal requests
Note that the FullAjaxExceptionHandler does not deal with normal (non-ajax) requests at all. To properly handle JSF and EL exceptions on normal requests as well, you need an additional FacesExceptionFilter. This will extract the root cause from a wrapped FacesException and ELException before delegating the ServletException further to the container (the container will namely use the first root cause of ServletException to match an error page by exception in web.xml).
So, all you need to do is to install the FacesExceptionFilter in order to get the same unwrapping behavior as FullAjaxExceptionHandler.

Spring MVC 404 even though URL is mapped

I've been trying to setup a Spring MVC controller but when I try to make a GET request, I get a 404 error.
I created a working test example here: https://github.com/Jardo-51/zk-spring-mvc-test
When I run the application on Tomcat and try to make a GET request to: http://localhost:8080/zk-spring-mvc-test/api/v0/foo, I get a 404 error and the logs say:
WARNING: No mapping found for HTTP request with URI [/zk-spring-mvc-test/api/v0/foo] in DispatcherServlet with name 'dispatcher-api'`
I've been trying to fix it according to this answer, and found out that the controller is mapped correctly because the logs on startup say:
INFO: Mapped "{[/zk-spring-mvc-test/api/v0/foo],methods=[GET]}" onto public org.springframework.http.ResponseEntity<java.lang.String> com.jardoapps.zkspringmvctest.controllers.FooController.method()
The app uses ZK framework which needs its own servlets so maybe there is a conflict with the DispatcherServlet. Please see my example app for more details (it contains only the necessary code).
Here is the web.xlm (Spring context and MVC config are at the top).
Here is the controller class.
Simply replace #RequestMapping("zk-spring-mvc-test/api/v0/foo") with #RequestMapping("/v0/foo") in your FooController class.
The reason is that the path that you specify into the #RequestMapping annotation is the part of the request's URL beyond the part that called the servlet.
You defined DispatcherServlet's mapping as:
<servlet-mapping>
<servlet-name>dispatcher-api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
So we have zk-spring-mvc-test that is the context root (this is deploy dependent), /api/ that calls the Spring DispatcherServlet, and finally /v0/foo that should be mapped by your controller:
#RestController
#RequestMapping("/v0/foo")
public class FooController {
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<String> method() {
return ResponseEntity.ok().body("OK");
}
}
You can see Spring MVC configure url-pattern for further information.

#ExceptionHandler + #ResponseStatus

I am handling my controlled exceptions using the following code:
#ExceptionHandler(MyException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ModelAndView handleMyException(MyException e) {
ModelAndView mav = new ModelAndView(ERROR_PAGE);
(...)
return mav;
}
That is, I want to both use custom views for different errors AND use response status code for the HTTP response.
At the same time, for pure 404 I have the following config in web.xml
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>400</error-code>
<location>/400</location>
</error-page>
Which takes to a 404 specific view.
The problem is that when a NOT_FOUND is thrown from my #ExceptionHandled method, it is not showing my custom view, debugging shows that execution actually goes through the handleMyException method, but after it's done it also goes through the method that maps the /404 in web.xml, and that is the view that gets shown.
Also if I throw a different Response Code, I get the default behavior on Exceptions, instead of my custom view.
I can't reproduce your problem with Tomcat 6 ans Spring 2.3.4. That is correct, because accroding to Servlet specification 2.5, the deployment descriptor defines a list of error
page descriptions. The syntax allows the configuration of resources to be returned
by the container either when a servlet or filter calls sendError
on the response for specific status codes (...)
I tracked where Spring sets response code basing on #ResponseStatus(HttpStatus.NOT_FOUND)
It is here:
public class ServletInvocableHandlerMethod (...)
private void setResponseStatus(ServletWebRequest webRequest) throws IOException {
if (this.responseStatus == null) {
return;
}
if (StringUtils.hasText(this.responseReason)) {
webRequest.getResponse().sendError(this.responseStatus.value(), this.responseReason);
}
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
// to be picked up by the RedirectView
webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, this.responseStatus);
}
In my case if error handler method is annotated
#ResponseStatus(HttpStatus.NOT_FOUND)
the following branch is selected:
else {
webRequest.getResponse().setStatus(this.responseStatus.value());
}
Because HttpServletResponse.setStatus is called and NOT HttpServletResponse.sendError, web container ignores error page defined in <error-code>404</error-code>
I hope my explanation will be useful to track the problem yourself. I suspect somewhere HttpServletResponse.sendError is called and it triggers web container to return default error page
It sounds like the problem is probably that the web container is trying to handle the 400/404's its seeing from the web application (because it doesn't understand the context of those errors). You probably need to get rid of the web.xml error page definitions and add more configuration to the Spring controllers to handle the generic 400/404 errors as well.
This guide helped me a lot when I was setting up exception handling in my app: http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
The web.xml tells the app container how to handle various response codes that are generated by the application. When you get an exception out of a controller method, it gets handled by the Spring #ExceptionHandler annotated method. At this point, the app container isn't involved so it has no idea what's going on yet.
My best understanding is that when you generate a 404 Http status from the exception handler method and return, Spring's basically done at that point, and the app container steps back in and says "I got a 404, what do I do with a 404? ah, redirect to /404". And then, control goes back to the web app itself to handle the /404 request.

No mapping found for HTTP request

I am back with working in Springs. I used to work in Springs but blindly, didn't understand much. I used to get a lot of errors, very basic ones, and I am getting them again.
My problem is that, I don't know how the configuration of the Spring-MVC work.
What happens when I run the project from my STS?
I am working on the spring template project in STS.
I am getting this when I run the project.
WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/common/] in DispatcherServlet with name 'appServlet'
I am totally fed up and broken.
Just 2 months of break from work, I am back at the starting block.
I don't want to post my code and make the question specific.
I want an answer that explains the way in which the server executes a spring project. Right from the running of an application(basic hello world application) to the display of the home page.
This will be helpful for all the beginners.
I tried searching for such an explanation in the net but I didn't get any proper explanation, but got a lot of basic samples. Those samples are easy to understand but are not explaining the way in which the server goes about.
Note: I am looking for an answer that explains the Springs concept. From the running of an application to the display of a home page. What all happens in this process? Where does the server start with? How does it go about?
Here is the flow initially servlet container loads the web.xml file.In web.xml we will specify that all the requests are handled by the spring FrontController that is DispatcherServlet.
We include it by adding the following code
<servlet>
<servlet-name>dispatcher</servlet-name>
<servletclass>org.springframework.web.servlet.DispatcherServlet</servletclass>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Here it indicate if the url request is of *.htm it is handled by dispatcherServlet then dispatcherServlet load dispatcher-servlet.xml . Where we need to mention the mapping to controller by writing the specific url request such as
<bean name="/insert.htm" class="com.controller.MyController"></bean>
So in bean we mention that for request of /insert.htm it tells the servlet to look in the mentioned class.You need use the Annotation of #RequestMapping above the method for ex
#RequestMapping("/insert.htm")
public ModelAndView insert(HttpServletRequest req,Student student)
{
String name=req.getParameter("name");
int id=Integer.parseInt(req.getParameter("id"));
student.setId(id);
return new ModelAndView("display","Student",student);//It returns a view named display with modelclass name as `Student` and model object student
}
So when a Request url of /insert.htm appears it executes the above method it returns a ModelAndView object nothing but an view.It again goes to dispatcher-servlet.xml and looks for view Resolver the normal code that is to be added is
<bean id="viewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver"
p:prefix="/WEB-INF/jsp/"
p:suffix=".jsp" />
So from this it gets the logical view name and appends the prefix and suffix to it .Finally it displays the content in the view.so it looks for display in view resolver prefixes and suffixes the things and finally returns /WEB-INF/jsp/display.jsp .Which displays the jsp content
You are mapping your Spring servlet only for requests that end with .htm. The request for the root of your application does not end with .htm and so, it does not get picked up by Spring. Edit your web.xml as follows, in order to use Spring for all requests:
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
Then, use this as the controller:
package com.mkyong.common;
#Controller
public class HomeController {
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView helloWorld() {
ModelAndView model = new ModelAndView("index");
model.addObject("msg", "hello world");
return model;
}
}
The controller intercepts the requests for the context root of the application, adds the msg attribute to the model and redirects to the index view.
So, you need to add the index.jsp file in the /WEB-INF/views/ directory. Inside your jsp, you will be able to use the value of the msg attribute.
From what every you have posted you do no have a request mapping for the url /common/.
You will have to create another request mapping function like the one below in your controller class and create a view file also.
#RequestMapping(value = "/common/", method = RequestMethod.GET)
public ModelAndView common(HttpServletRequest request,
HttpServletResponse response) {
ModelAndView model = new ModelAndView("common");
model.addObject("msg", "hello world");
return model;
}

Resources