Tuesday, December 31, 2013

Another way to get around the Spring MVC 404 exception

A lot of applications are set up without a default for displaying a home page - even if they have one. This (excellent) tutorial is a case in point:

http://codetutr.com/2013/04/09/spring-mvc-easy-rest-based-json-services-with-responsebody/

I managed to get it running in eclipse, but of course I got the page not found, 404 exception. With my recent research, however, I didn't feel quite as helpless as I usually do when faced with this. After checking out my last post, I realized what was lacking in this application was a default request mapping - i.e.


@RequestMapping({"/","/home"})

Since it came with a "home.jsp" already, I just copied a method from the STS-generated springMVC app:

public String home(Locale locale, Model model) {

      return "home";

}

If there wasn't a home.jsp there already, I could have copied one into WEB-INF/views from the generated project. 

Here's the page:



How to debug STS's SpringMVCProject template

I've recently decided to get more into learning Spring again, this time from an MVC angle. From time to time, I've tried to create a spring mvc project from spring's IDE, STS.  For me,  it's been very unreliable, always coming up with "404" exceptions. 

So, I'm going to do it again, with the idea to debug any issues based on what I've learned. First, go to STS, click File > New > Spring Template Project, then choose "Spring MVC Project" as the template.  After that, give it a name like "mvcTest" and a package name like "com.acme.mvctest". Build it and run it on the server. 

Of course, when I try it now, it works perfectly. But, before I was having issues with it, I promise. Specifically. a 404 page not found message for "http://localhost:8080/mvctest/". 

Also, in the log would be a message something along the lines of: 
"WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/mvctest/] in DispatcherServlet with name 'appServlet".

To fix it, I had to be sure of a couple of things:

1) the url mapping in web.xml needs to be "/", e.g.

<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>


2. Check the @RequestMapping annotation in the controller.  

It should: 

"@RequestMapping({"/","/home"})," 

because, since "home" is returned, it's looking for {apppath}/home.jsp per the view resolver.  But the controller is defaulting to this: 

"@RequestMapping(value = "/", method = RequestMethod.GET)"

So it's not finding  a mapping to "home" unless you put it in, usually. In this case it did for some reason.

On a side note, I ran into a problem trying to debug the 404 message when I put in "/*" as the servlet url mapping.  After a lot of checking around, I found this post in stackOverflow:

http://stackoverflow.com/questions/1266303/no-mapping-found-for-http-request-with-uri-web-inf-pages-apiform-jsp

Basically, the view dispatcher was correctly retuning the jsp page requested, but the '/*' meant the forwarded view was captured by DispatcherServlet as if it were a request! Since there's no handler specified, it came up with "No mapping found" 404 error. 

Another problem I ran into was some code which didn't include a method with 
"@RequestMapping(value = "/", method = RequestMethod.GET)" annotation. So, I just added one, and it worked, defaulting to that page and avoiding a 404 issue when first deploying it.  

So, that's my takeaway lesson. Always have a

"@RequestMapping(value = "/", method = RequestMethod.GET)"

in then controller, and map the Dispatcher servlet to the url "/" in web.xml.  Or, at least map to a url which won't get confused with .jsp, such as "*.do" or "*.htm".