Wildcards in requestmapping - java

I am working on my Spring application and as per below code, I am mapping multiple URL's to one method. I am making one method "root" to serve all the request which starts with index and root.
#RequestMapping(value = {"/", "index*"}, method = RequestMethod.GET)
public String root(Model model) {
logger.info("Welcome to index page.");
model.addAttribute("hello", "Welcome to index page." );
return "index";
}
The above "index*" is working fine in case of "index", "index123", "index.html" and "index.txt" and request is directed to the mapped method i.e. "root" but its not working for "index.jsp". In case of "index.jsp" i am getting "HTTP Status 404" "The requested resource is not available".
Excerpt from web.xml
<servlet>
<servlet-name>appServlet</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>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Can someone let me know the reason behind this and how can I make it work?

I think this may be happening because the servlet container maps *.jsp requests to the JspServlet in order to compile and execute the jsp. If you're using Tomcat this happens in the top level web.xml in Tomcat's conf directory. This would mean that requests ending in *.jsp would be intercepted before they reached your controller. The JspServlet would attempt to load the index.jsp file based on the path in the webapp and would return a 404 when it couldn't find it.
For /index.jsp, you may do best to actually create this file in the root of the webapp so that you don't see a 404. You could add some code to it to redirect requests somewhere else (perhaps to /index.html - or to some other entry point of your application).

Related

About Welcome Page and Accessing Web Service Methods

I have a problem with configuration ( or basic understanding how things work at background). I create a JAVAEE project by checking Web application and ReSt api checkbox ( in intellij with glassfish 5.0). I have sample code below which web methods work but welcome page does not work. My web.xml and sample web service methods are below.
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>test</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
**<url-pattern>/ *</url-pattern>**
</servlet-mapping>
#Path("/RestTest")
public class TestString {
#Context
ServletContext context;
#GET
#Path("insertdb/{param1}/{param2}")
#Produces(MediaType.APPLICATION_JSON)
public Object writeToDb( #PathParam("param1") String param1
,#PathParam("param2") String param2){
try{
String password= context.getInitParameter("DbPassword");
Class.forName("org.mariadb.jdbc.Driver");
Connection dbCon = DriverManager.getConnection(
"jdbc:mariadb://xxx/testdb", "root", password);
PreparedStatement stmt=dbCon.prepareStatement(
"INSERT INTO TestTable VALUES(?,?)");
stmt.setString(1,param1);
stmt.setString(2,param2);
stmt.executeUpdate();
dbCon.close();
return "Success";
}catch(SQLException | ClassNotFoundException ex){
return ex.toString();
}
}
#GET
#Path("sum/{sum1}/{sum2}")
#Produces(MediaType.TEXT_HTML)
public String calculateSum(#PathParam("sum1") int param1
,#PathParam("sum2") int param2){
return ""+(param1 + param2);
}
If i change this line url-pattern "/*" to "/"
then welcome page is accessible but not methods.
Thus what i want is, having a welcome page which i will use for documentation for my web services(i dont want SOAP) and web methods must work by adding / to base url. How can i achieve that and what is difference between /* and /
See here for explanation of differences:
What is url-pattern in web.xml and how to configure servlet
Generally for a rest api it is best to use a path specific to all rest calls, for instance http://localhost/mywebapp/rest/...
Something like:
<servlet-mapping>
<servlet-name>jersey-servlet/servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You only want jersey serving particular URLs when it is bundled in a WAR that also includes html pages.
To answer your question, difference between "/" and "/*"
A mapping that contains the pattern "/" matches a request if no other pattern matches. This is the default mapping. The servlet mapped to this pattern is called the default servlet. The default mapping is often directed to the first page of an application. Example :
Both requests will display same contents from index.jsp
http://myhost.com/index.jsp
and
http://myhost.com/
Now, a mapping that contains "/*" overrides all other servlets, including all servlets provided by the servlet container such as the default servlet and the JSP servlet. Whatever request you fire, it will end up in that servlet. This is thus a bad URL pattern for servlets.
Possible solution for your issue
Change the URL pattern to specific instead of default pattern.
<servlet>
<servlet-name>webservice</servlet-name> //servlet name
<servlet-class>com.rest.MyRestServlet</servlet-class> //servlet class
</servlet>
<servlet-mapping>
<servlet-name>webservice</servlet-name> //servlet name
<url-pattern>/RestTest/*</url-pattern> //all webservice request
</servlet-mapping>
All the web service request are accessible through
http://myhost.com/RestTest/
You may also be interested to look
What is URL-pattern in web.xml and how to configure servlet
Basics of Java Servlet
Servlet configuration and url-pattern
As you highlighted, your problem revolves around those four lines:
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
As Jim Weaver mentioned, it's a matter of url-pattern.
Solution(s)
You may consider three solutions (from the most-preferred to least-preferred):
dedicated REST URL: the easiest way is to have a dedicated url-pattern such as /rest/* for your web services. You can benefit some assets such as url hierarchy or you can easily implement a security framework over it.
URL rewriting may be an option and this answer suggests some library. I haven't tested those libraries myself
Page redirection can be an option to go around REST filtering but in the specific case of having the url-pattern at /*, I have to say I'm not sure if it's working for the reason I'll explain in next section
now a bit of explanation of what happened
Why setting the url-pattern at /* prevent from accessing the welcome page?
Actually, it's not only the welcome page that is not accessible: it's all the resources under the defined url-pattern. Whatever get in touch with REST stays with REST... Here is the schema taken from JSR 339 Appendix C:
With a GlassFish 5.0, I guess you're using JAX-RS 2.1 (JSR 370) but the sections I'm quoting have the same content
Without entering into detail, it is visible that only ContainerRequest Filters are executed in the process. Especially, it's worthy to notice that after Request Matching, requests are not forwarded to any servlet in a sense that standard resources are not reachable, unless specified by the REST method.
It's also worthy to highlight the fact the servlet filters are executed beforehand (leveraging this point is absolutely vital for managing security for example). I did not find back the source proving this point but I know it's somewhere on the web ^^.
Request matching is defined at section 3.7.2. In a nutshell, it is in three steps where the first one is the most important for your question, especially at step D:
Identify a set of candidate root resource classes matching the request
c. ...
d. If [the set of classes whose path matches the request URL] is empty then no matching resource can be found, the algorithm terminates and an implementation MUST generate a NotFoundException (404 status) and no entity
e. ...
highlights are mine.
The two last steps are
Obtain a set of candidate resource methods for the request
Identify the method that will handle the request
TL;DR
What happened when you set <url-pattern>/*<url-pattern> and tries to access to your welcome page (or any page actually):
Your server receives the GET request
If there are filters (such those from a security framework), there are executed
REST enters the scene
Pre Match filters are executed (none if your case)
Fetch your root resources classes (in your example, all classes in the test package)
Find if one of those class match the request URL
None are found, a 404 status is returned
To avoid unnecessary URL conflicts, the best options would be having a dedicated URL for your REST methods
If you mention in web xml like following all the request receive by 'Jersey Web Application' servlet. so request to index.jsp also redirect to 'Jersey Web Application' servlet
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
To avoid this add some prefix to the url to separate rest request like following
<servlet-mapping>
<servlet-name>Jersey Web Application</servlet-name>
<url-pattern>rs/*</url-pattern>
</servlet-mapping>

RequestMapping to accept all urls in Rest Apis

I have a Controller which is designed to accept all requests and do all the required processing. However it is not working for all requests. My Controller is such:
#RequestMapping("/*")
public class GatewayDummyController
and my web.xml configuration is:
<servlet-name>sample-apis-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<servlet-mapping>
<servlet-name>sample-apis-servlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
The name of the war is sampleApi. I am able to access urls:
localhost/8080/sampleApi
localhost/8080/sampleApi/rest
the URL that I cannot access is:
localhost/8080/sampleApi/rest/v1.
I need to access all urls after sampleApi/. Please suggest a way to do that.
Add #RequestMapping("/**") to the controller to accept all urls

Spring error: No mapping found for HTTP request with URI [/myproject/] in DispatcherServlet with name 'appServlet'

After setting up the default security page with Spring security (in memory), I receive the following error:
No mapping found for HTTP request with URI [/myproject/] in DispatcherServlet with name 'appServlet'
Update:
This problem was fixed by moving the component-scan (which scans my security package) from the servlet-context.xml to root-context.xml
You error messaege tells No mapping found for HTTP request with URI [/addressbook/]. In other words, you have not mapped a request mapping for the URL /addressbook
If you want your project to have a base URL as /addressbook then change the web.xml as below
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
....
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/addressbook/*</url-pattern>
</servlet-mapping>
Controller
#Controller
public class HomeController {
#RequestMapping(value="/", method = RequestMethod.GET)
public String home(Locale locale, Model model) {
return "home";
}
}
Then goto the browser and type the below. There should be a / at end.
http://hostname:port/addressbook/
But if you want to change the context root, you cannot do this by web.xml. Context root isn't part of the standard web.xml file. You can change this by doing the following in the eclipse
In project properties click on Web Project Settings (assuming you are in a dynamic web project.) The only configuration value there is "Context root:"
Change "Context root" to /addressbook
Change the web.xml url-pattern to <url-pattern>/</url-pattern>
rebuild
remove the project from tomcat
redeploy.
Quoted from digitaljoel

should I make two different servlet entry for rest and normal html in web.xml

I am writing spring mvc application.
In my application I have web pages as well as rest web services to handle ajax call.
I have done below entry in web.xml
<servlet>
<servlet-name>myapp</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring_myapp-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>*.html</url-pattern>
</servlet-mapping>
Should I map my rest url with same servlet like
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
Or should I make new servlet entry for rest.
I have done required entries in pom.xml for "org.codehaus.jackson" and also I have made required entries in my spring_myapp-servlet.xml.
For html page I am using below code in my controller
#RequestMapping(value = "/htmlUrl")
public ModelAndView ModifyValiodation(HttpServletRequest request) {
// my code
}
For rest service I am using
#RequestMapping(value = "/restUrl")
public #ResponseBody Map<String, String> restUrl(HttpServletRequest request) {
// my code
}
If I am using only one servlet for two url mapping, then total 4 url will be made.
myapp/htmlUrl.html
myapp/restUrl.html
myapp/rest/htmlUrl
myapp/rest/restUrl
If I am using two different servlet with individual dispacherServlet then will i have to make entry of every component and service of spring in both the servlet.xml?
Please point out the solution for exposing rest web service.
Thanks!
use
<servlet-mapping>
<servlet-name>myapp</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
If you use two DispatcherServlet entries , it will load two ApplicationContext Objects in your application. Since you are using spring mvc to handle all the requests to your app, you should be fine with this configuration. Any request url that ends with .html or any urls that contains /rest/ will be handled by spring.
It is up to you to design the server side of the infrastructure.
Neither the RESTful specifications have any instructions for doing this nor the Servlet specifications enforce anything on this.
On the Applications design I think it is better idea to keep two different servlets to handle different URLs because over time the classes will become complex and long. These to may be used as front controllers and may have common logic class in the backend.

how to configure servlet mapping

I have spring mvc application
if in web.xml i write so:
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I go to http://localhost:8080/Mvc/controllerPath/sayHello
I see my page
if I write
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/controllerPath/*</url-pattern>
</servlet-mapping>
I go to http://localhost:8080/Mvc/controllerPath/sayHello - I see 404
I think you understood what I want.
Can you hel me?
UPDATE
controller:
#Controller
#RequestMapping("/controllerPath")
public class MyController {
#RequestMapping("/sayHello")
public String sayHello(Model model){
model.addAttribute("name", "Vasya");
return "hello";
}
}
if I write
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/Mvc/controllerPath*</url-pattern>
</servlet-mapping>
i see 404
If you remove:
#RequestMapping("/controllerPath")
to
#RequestMapping("/")
the new servlet mapping will work.
The reason for this is that whatever you have in the servlet mapping url is stripped before spring tries to match it to a controller.
For example, in your first url mapping with just / (and assuming your web appllication is deployed to /mvc), your path of /mvc/controllerPath/sayHello spring strips the url mapping away from the url and expects to find a controller that maps to /controllerPath/sayHello
When you change the url-pattern to /controllerPath, since will strip that as well and look for a controller that answers to just /sayHello, which your controller won't since it's expecting /controllerPath/sayHello

Categories