I have two pages: home.jsp, and stylechoosertable.jsp. home.jsp has a simple link going to stylechoosertable.jsp. Both are in src - main - webapp - views. Starting the app runs fine, loading home.jsp is fine. However, when I click the link, it goes to a 404 Not Found page.
Here is HomeController.java
#Controller
public class HomeController {
private static final Logger logger = LoggerFactory.getLogger(HomeController.class);
/**
* Simply selects the home view to render by returning its name.
*/
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
logger.info("Welcome home! The client locale is {}.");
return "home";
}
}
Here is CSSTableController.java
#Controller
public class CSSTableController {
private static final Logger logger = LoggerFactory.getLogger(CSSTableController.class);
private final CSSService cssService;
#Autowired
public CSSTableController(CSSService cssService) {
this.cssService = cssService;
}
#RequestMapping(value = "/stylechoosertable.jsp", method = RequestMethod.GET)
public List<StyleChooser> get() {
return cssService.getAllStyleChoosers();
}
}
servlet-context.xml
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
web.xml
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
A couple of things I've noticed:
Only local:8080/myspringmvc/ brings up the home page. local:8080/myspringmvc/home.jsp brings up the same 404 error, which I don't get.
On start, I can see it doing the Request/Handler mappings properly:
INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/stylechoosertable.jsp],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.util.List com.css.genapp.CSSTableController.get()
INFO : org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping - Mapped "{[/],methods=[GET],params=[],headers=[],consumes=[],produces=[],custom=[]}" onto public java.lang.String com.css.genapp.HomeController.home(org.springframework.ui.Model)
It doesn't throw any org.springframework.web.servlet.PageNotFound errors when I go to home.jsp or stylechoosertable.jsp, so I know the mapping is there.
I can't figure out why it doesn't "hit" my stylechoosertable.jsp. Any ideas?
Well I don't think I am able to get your question properly, still if you want to have simple navigation function from home.jsp, i.e on click of the link, it should go to stylechoosertable.jsp, then a definite solution would be just have a link like
SomeName
Now simply map this in controller
#RequestMapping(value = "/stylechoosertable")
public String goToSCT() {
return "stylechoosertable";
}
And if there is any stylechoosertable.jsp available in /WEB-INF/views/, it will simply get called.
Second Question the reason behind local:8080/myspringmvc/ working, not local:8080/myspringmvc/home.jsp is basically lack of RequestMapping for /home.jsp, where as for the root URL i.e /, a mapping is there in HomeController
Most Servlet containers register a Servlet implementation to handle JSPs. They would look like
<servlet>
<servlet-name>jspServlet</servlet-name>
<servlet-class>com.example.JspServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>jspServlet</servlet-name>
<url-pattern>*.jsp</url-pattern>
</servlet-mapping>
The *.jsp is known as an extension mapping. A Servlet container knows about this Servlet and the ones you've registered. The container doesn't know anything about your #Controller beans. That are relevant to Spring only.
When a Servlet container receives a request, it goes through an order specified in the Servlet specification for matching a particular Servlet based on its mapping(s). Basically, it first checks for an exact match, then it tries to do path matching (patterns like /something/*), then it does extension mapping, and finally, if no matches were found, uses the default servlet, a servlet mapped to /. In this case, your DispatcherServlet (which runs the Spring MVC stack) is mapped to /, so it is the default servlet.
When you send your request to
local:8080/myspringmvc/home.jsp
the path satisfies the extension mapping of *.jsp and therefore the container uses the JspServlet to handle the request. This JspServlet tries to find an appropriate resource, but doesn't. Remember that JSPs inside WEB-INF are not accessible publicly/externally. It therefore returns a 404.
When you send your request to
local:8080/myspringmvc/
the default servlet is chosen because nothing else can match that path. The DispatcherServlet looks up its handlers and finds
#RequestMapping(value = "/", method = RequestMethod.GET)
public String home(Model model) {
logger.info("Welcome home! The client locale is {}.");
return "home";
}
By returning the view name home and because you've registered the appropriate InternalResourceViewResolver, Spring will do a RequestDispatcher#forward(..) to that JSP. That request will again be matched by the Servlet container, but this time, to the JspServlet. Because this is an internal call (and the path is right), the request will be completed.
Similarly, for /stylechoosertable.jsp, the JspServlet is chosen before your DispatcherServlet could do anything about it.
It doesn't throw any org.springframework.web.servlet.PageNotFound
errors when I go to home.jsp or stylechoosertable.jsp, so I know the
mapping is there.
The DispatcherServlet is never involved, so it doesn't have the chance to log anything.
Related
I have write a controller based on Spring MVC.
#Controller
#RequestMapping("/hello")
public class JsonController {
#RequestMapping(value="/",method=RequestMethod.GET)
#ResponseBody
public Person service(){
Person person=new Person();
person.setId(3);
person.setName("666");
return person;
}
When I access "http://localhost/app/hello",I get 404;
When I access "http://localhost/app/hello/", I get 202 OK.
What's the difference between "http://localhost/app/hello" and "http://localhost/app/hello/"?
look your controller code
#RequestMapping("/hello")
public class JsonController
you controller have url mapping -> "/hello"
and action(service) url mapping is "/"
#RequestMapping(value="/",method=RequestMethod.GET)
#ResponseBody
public Person service()
Now whenever we provided the mapping for our controller then every action of controller need the controller URL path as prefix (if URL mapping is defined in controller), as you have mentioned your controller mapping with "/hello" and action service url mapping with "/"
so when you need to access the service action then ->
you need basepath of the controller (if Request URL mapping define in the controller) + action URL mapping
-> "/hello" + "/" => "/hello/"
so in the case of access URL "http://localhost/app/hello/" its easily find service action and returned the response
Now when you trying to access URL "http://localhost/app/hello", URL mapping search this mapping and find it in your controller mapping(because of its defined in your case) but there is no action defined for it that why are getting 404.
You can define it default action like:
#RequestMapping(method=RequestMethod.GET)
public Person defaultAction() {
----your code
}
so now if you will hit "http://localhost/app/hello" this your will return valid response not 404
I guess, it is something to do with your tomcat redirect configuration. Try including the below attributes in your context.xml
mapperContextRootRedirectEnabled
Desc: If enabled, requests for a web application context root will be redirected (adding a trailing slash) if necessary by the Mapper rather than the default Servlet. This is more efficient but has the side effect of confirming that the context path exists. If not specified, the default value of true is used.
mapperDirectoryRedirectEnabled
Desc: If enabled, requests for a web application directory will be redirected (adding a trailing slash) if necessary by the Mapper rather than the default Servlet. This is more efficient but has the side effect of confirming that the directory is exists. If not specified, the default value of false is used.
Reference: https://tomcat.apache.org/tomcat-7.0-doc/config/context.html#Context_Parameters
I have a Spring (v, 4.3.2 & Java 8) application that serves up an Angular2 single page application front end that has multiple routes (say /foo, /bar and /baz, one of which requires a parameter to render (/baz/x where x is the identifier of the resource to retrieve).
The html is served up with a standard Spring MVC #Controller:
#RequestMapping(value = {"/", "/foo", "/bar", "/baz/{id}"}, method = RequestMethod.GET)
public String index(#PathVariable Optional<String> id) {
return "index";
}
and views are resolved using:
#Bean
public InternalResourceViewResolver internalResourceViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setPrefix("WEB-INF/pages/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
If the browser reloads from the /, /foo or /bar routes, the controller properly returns the index page and angular resolves the view for the route as expected. However, if the browser reloads from one of the baz routes that contain a paramenter, the server prepends baz to the resource path (e.g. /baz/WEB-INF/pages/index.jsp) which, results in a 404 error.
I've tried refactoring the baz request mappings info into an annotation on a distinct (identical) method and using wildcards in the request mapping (e.g. /** and baz/* ), but always get the same result. Regardless of the request mapping, Spring seems unaware that it is appending baz into the path, as the logs contain:
DEBUG: org.springframework.web.servlet.DispatcherServlet - Rendering view [org.springframework.web.servlet.view.InternalResourceView: name 'index'; URL [WEB-INF/pages/index.jsp]] in DispatcherServlet with name 'dispatcher'
DEBUG: org.springframework.web.servlet.view.InternalResourceView - Forwarding to resource [WEB-INF/pages/index.jsp] in InternalResourceView 'index'
DEBUG: org.springframework.web.servlet.DispatcherServlet - Successfully completed request
Any help on this would be appreciated.
Solved the problem - omitting the leading slash from the prefix argument string causes the issue. For proper configuration the argument should be:
viewResolver.setPrefix("/WEB-INF/pages/");
When I go to the first url, it calls my home() method in the controller but when I go to the second url it does not call my homeTest() method. Why is that?
I get 404 error.
http://localhost:9083/MYAPP/foo ------ first url
http://localhost:9083/MYAPP/foo/bar ------ second url
web.xml
<servlet>
<servlet-name>springServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/appServlet/servlet-context.xml</param-value>
</init-param>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springServlet</servlet-name>
<url-pattern>/foo/*</url-pattern>
</servlet-mapping>
Controller:
#RequestMapping(value="/foo", method = RequestMethod.GET)
public String home(Model model){
return "home";
}
#RequestMapping(value="/foo/bar", method = RequestMethod.GET)
public String homeTest(Model model){
return "home";
}
You need to configure your RequestMappingHandlerMapping.
The official documentation goes into detail about handler mappings and some of their properties. The relevant one here is alwaysUseFullPath:
alwaysUseFullPath If true, Spring uses the full path within the current Servlet context to find an appropriate handler. If false
(the default), the path within the current Servlet mapping is used.
For example, if a Servlet is mapped using /testing/* and the
alwaysUseFullPath property is set to true, /testing/viewPage.html
is used, whereas if the property is set to false, /viewPage.html is
used
In short, when trying to find a mapping for /foo/bar, it removes the part that was matched by the Servlet environment, the /foo, and only uses the /bar to find a handler. You have no handler mapped to /bar.
By setting the property above to true, it will use the full path.
You can configure this in a #Configuration annotated WebMvcConfigurationSupport subclass, by overriding requestMappingHandlerMapping
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping handlerMapping = super.requestMappingHandlerMapping();
handlerMapping.setAlwaysUseFullPath(true);
return handlerMapping;
}
Or whatever mechanism is appropriate for your configuration (there's an XML equivalent for example).
There's a special case for the exact match of /foo. It's not particularly relevant here.
Just change:
<url-pattern>/foo/*</url-pattern>
To
<url-pattern>/foo/**</url-pattern>
Basically I have a mapping like /A/B/something whose mapping is given as:
#Controller
#RequestMapping("/B")
public class BController {
...
#RequestMapping(value = "/something", method = RequestMethod.POST)
public ModelAndView func1()....
func1() gets called.
In web.xml, the definition is given only for A.
So its something like:
<servlet>
<servlet-name>A</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>A</servlet-name>
<url-pattern>/A/*</url-pattern>
</servlet-mapping>
In applicationcontext.xml, there is component scan for this BController.
<context:component-scan base-package="BController" />
So, my question is:
How does servlet know to move from /A's mapping to /B's mapping. How
is func1() getting called?
If applicationcontext.xml directly takes /B into
consideration, why is func1() not getting called after I delete /A from the call ( If I call /B/something from my application, it
gives an error. )?
Any help is appreciated.
Thanks :)
The spring DispatcherServlet is a central component which dispatches all requests to the registered handlers (the controllers). In your case, it has been mapped to the /A relative path:
<url-pattern>/A/*</url-pattern>
The line above basically tells the web container, that each and every request which starts with /A relative to the host should be handled by the spring DispatcherServlet. From this point further, it's up to Spring to handle the mappings.
#RequestMapping("/B")
The line above adds up to the DispatcherServlet, so to this point it maps to /A/B
#RequestMapping(value = "/something")
Similar to the previous request mapping, the line above adds up to the class level mapping, so the method func1 will finally be called with the following call:
POST <yourhost>/A/B/something
Hope that helps.
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/test/*</url-pattern>
</servlet-mapping>
If I hit /test/page the above will work. However, hitting /test or /test/ will not work. I'm using Spring MVC, and my request mapping is as follows:
#RequestMapping(value = {"","/"})
EDIT:
I'm in the process of verifying with an independent project, but this appears to be a bug with Spring's UrlPathHelper. The following method returns an incorrect path when there is both a context and a servlet path, and you hit the servlet without a trailing slash.
public String getPathWithinApplication(HttpServletRequest request) {
String contextPath = getContextPath(request);
String requestUri = getRequestUri(request);
if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
// Normal case: URI contains context path.
String path = requestUri.substring(contextPath.length());
return (StringUtils.hasText(path) ? path : "/");
}
else {
// Special case: rather unusual.
return requestUri;
}
}
Just as an example let's say I have a context of "admin" and the following servlet-mapping:
<servlet-mapping>
<servlet-name>usersServlet</servlet-name>
<url-pattern>/users/*</url-pattern>
</servlet-mapping>
Now I have a request mapping in one of my controllers like this:
#RequestMapping(value = {"","/"})
If I hit /admin/users it will not work. However, if I hit /admin/users/ it will work. Now if I change my request mapping to the following then they will both work:
#RequestMapping(value = {"/users","/"})
However, now the URL /admin/users/users will also work (which is not what I would want).
Yevgeniy is correct, but if your DispatcherServlet is taking over for the default servlet, you have to add this to your web.xml:
<welcome-file-list>
<welcome-file>/</welcome-file>
</welcome-file-list>
my setup usually looks like this:
<servlet-mapping>
<servlet-name>testServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
controller, where i assume you want to handle /test and /test/ equally:
#Controller
public class MyController {
#RequestMapping("/test")
public String test() {
return "redirect:/welcome";
}
#RequestMapping("/test/")
public String test() {
return "redirect:/welcome";
}
#RequestMapping("/welcome")
public void test(ModelMap model) {
// do your stuff
}
}
setup like this would cause DispatcherServlet to handle requests for *.css and *.js files, which is not desired in most cases. i think this is the problem Bhavik describes. For those resources you can you the ResourceController like this:
<mvc:resources mapping="/css/**" location="/resources/css/" />
<mvc:resources mapping="/js/**" location="/resources/js/" />
files from /resources/css and /resources/js will be served without forcing you to write a extra controller.
First of all, the difference between mapping dispatcher servlet to "/" and to "/*".
There is a difference!
When mapping to "/*", all URL requests (including something like this "/WEB-INF/jsp/.../index.jsp") are mapped to dispatcher servlet.
Secondly, when using Spring + Tiles, and returning some JSP in your tiles definition, it is treated as an internal forward request, and handled by the same servlet as the original request.
In my example, I invoke root URL "/", which is properly caught by home() method, and then forwarded to "index.jsp" by Tiles, which is again being handled by Dispatcher Servlet.
Obviously, dispatcher servlet cannot handle "index.jsp", because there is no controller for it.
Yeah, it is ugly, but looks like this is the way it works.
So, the only solution I've found so far: to change "/*" back to "/" in web.xml. This way JSPs are rendered properly by Tomcat's jsp servlet, I guess, and not dispatcher servlet.
Unfortunately, this fix will break the ROOT URL dispatching by Spring, so you need to leave the idea of using ROOT URL + Tiles for now.
Please note that adding explicit servlet mapping ".jsp -> Tomcat jsp in web.xml doesn't help, when using "/*", and it sucks.
Still the problem is not resolved.
Also this is the problem in Spring MVC 3.0
A way without touch the web.xml file is by set the map to the default welcome file path.
#RequestMapping("/index.html")
In my case, every url was working except of the root "/" url.
The problem was that i didn't deleted the index.htm file inside of my projects' webapp root folder.