How to use #WebServlet to accept arguments (in a RESTFul way)? - java

suppose that I want to accept the following urls:
http://myserver/myapplication/posts
http://myserver/myapplication/posts/<id>
http://myserver/myapplication/posts/<id>/delete
how can I use the servlet decorator #WebServlet to do so? I'm investigating value and urlPatterns but I don't get how to do so. For example,
#WebServlet(urlPatterns={"/posts", "/posts/*"})
[..]
String param = request.getPathInfo();
gives me some result, but how to use it? Also, request.getPathInfo() seems to return the value of the wildcard, but what if I want more parameters like in http://http://myserver/myapplication/posts/<id>/delete/<force>?

In servlet specification, you have no notion of path variables. Some MVC frameworks do support them, for example Struts or Spring MVC.
For a servlet point of view, an URL is :
scheme://host.domain/context_path/servlet_path/path_info?parameters
where any of the parts (starting from context path may be null)
Spec for servlet 3.0 states :
Context Path: The path prefix associated with the ServletContext that this
servlet is a part of. If this context is the “default” context rooted at the base of the
Web server’s URL name space, this path will be an empty string. Otherwise, if the
context is not rooted at the root of the server’s name space, the path starts with a
/ character but does not end with a / character.
Servlet Path: The path section that directly corresponds to the mapping which
activated this request. This path starts with a ’/’ character except in the case
where the request is matched with the ‘/*’ or ““ pattern, in which case it is an
empty string.
PathInfo: The part of the request path that is not part of the Context Path or the
Servlet Path. It is either null if there is no extra path, or is a string with a leading
‘/’.
The following methods exist in the HttpServletRequest interface to access this
information:
getContextPath
getServletPath
getPathInfo
It is important to note that, except for URL encoding differences between the request
URI and the path parts, the following equation is always true:
requestURI = contextPath + servletPath + pathInfo
That means that you just have to use #WebServlet(urlPatterns={"/posts"}), and then decode by hands the pathInfo part to extract commands and parameters

I think you cannot do so using only the #WebServlet annotation. The urlPatterns only acts as a directive to the Servlet to indicate which url patterns should attend.
And as you can see by this docs https://docs.oracle.com/javaee/6/api/javax/servlet/annotation/WebServlet.html the value is just the case when urlPatterns is one string instead of an array of them.
As brso05 stated, you will need to parse from the request your parameters.

Related

Servlet crashes jsp layout [duplicate]

The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer 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. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.

Filter servlet causing image, css and JS not displaying on client while URL Pattern of Filter Servelet is /* and Servlet URL Pattern is / [duplicate]

The familiar code:
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>main</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
My understanding is that /* maps to http://host:port/context/*.
How about /? It sure doesn't map to http://host:port/context root only. In fact, it will accept http://host:port/context/hello, but reject http://host:port/context/hello.jsp.
Can anyone explain how is http://host:port/context/hello mapped?
<url-pattern>/*</url-pattern>
The /* on a servlet overrides all other servlets, including all servlets provided by the servletcontainer 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. Usually, you'd like to use /* on a Filter only. It is able to let the request continue to any of the servlets listening on a more specific URL pattern by calling FilterChain#doFilter().
<url-pattern>/</url-pattern>
The / doesn't override any other servlet. It only replaces the servletcontainer's built in default servlet for all requests which doesn't match any other registered servlet. This is normally only invoked on static resources (CSS/JS/image/etc) and directory listings. The servletcontainer's built in default servlet is also capable of dealing with HTTP cache requests, media (audio/video) streaming and file download resumes. Usually, you don't want to override the default servlet as you would otherwise have to take care of all its tasks, which is not exactly trivial (JSF utility library OmniFaces has an open source example). This is thus also a bad URL pattern for servlets. As to why JSP pages doesn't hit this servlet, it's because the servletcontainer's built in JSP servlet will be invoked, which is already by default mapped on the more specific URL pattern *.jsp.
<url-pattern></url-pattern>
Then there's also the empty string URL pattern . This will be invoked when the context root is requested. This is different from the <welcome-file> approach that it isn't invoked when any subfolder is requested. This is most likely the URL pattern you're actually looking for in case you want a "home page servlet". I only have to admit that I'd intuitively expect the empty string URL pattern and the slash URL pattern / be defined exactly the other way round, so I can understand that a lot of starters got confused on this. But it is what it is.
Front Controller
In case you actually intend to have a front controller servlet, then you'd best map it on a more specific URL pattern like *.html, *.do, /pages/*, /app/*, etc. You can hide away the front controller URL pattern and cover static resources on a common URL pattern like /resources/*, /static/*, etc with help of a servlet filter. See also How to prevent static resources from being handled by front controller servlet which is mapped on /*. Noted should be that Spring MVC has a built in static resource servlet, so that's why you could map its front controller on / if you configure a common URL pattern for static resources in Spring. See also How to handle static content in Spring MVC?
I'd like to supplement BalusC's answer with the mapping rules and an example.
Mapping rules from Servlet 2.5 specification:
Map exact URL
Map wildcard paths
Map extensions
Map to the default servlet
In our example, there're three servlets. / is the default servlet installed by us. Tomcat installs two servlets to serve jsp and jspx. So to map http://host:port/context/hello
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Doesn't match any extensions, next.
Map to the default servlet, return.
To map http://host:port/context/hello.jsp
No exact URL servlets installed, next.
No wildcard paths servlets installed, next.
Found extension servlet, return.
Perhaps you need to know how urls are mapped too, since I suffered 404 for hours. There are two kinds of handlers handling requests. BeanNameUrlHandlerMapping and SimpleUrlHandlerMapping. When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. One thing we need to know is these two handlers share a common property called alwaysUseFullPath which defaults to false.
false here means Spring will not use the full path to mapp a url to a controller. What does it mean? It means when you define a servlet-mapping:
<servlet-mapping>
<servlet-name>viewServlet</servlet-name>
<url-pattern>/perfix/*</url-pattern>
</servlet-mapping>
the handler will actually use the * part to find the controller. For example, the following controller will face a 404 error when you request it using /perfix/api/feature/doSomething
#Controller()
#RequestMapping("/perfix/api/feature")
public class MyController {
#RequestMapping(value = "/doSomething", method = RequestMethod.GET)
#ResponseBody
public String doSomething(HttpServletRequest request) {
....
}
}
It is a perfect match, right? But why 404. As mentioned before, default value of alwaysUseFullPath is false, which means in your request, only /api/feature/doSomething is used to find a corresponding Controller, but there is no Controller cares about that path. You need to either change your url to /perfix/perfix/api/feature/doSomething or remove perfix from MyController base #RequestingMapping.
I think Candy's answer is mostly correct. There is one small part I think otherwise.
To map host:port/context/hello.jsp
No exact URL servlets installed, next.
Found wildcard paths servlets, return.
I believe that why "/*" does not match host:port/context/hello because it treats "/hello" as a path instead of a file (since it does not have an extension).
The essential difference between /* and / is that a servlet with mapping /* will be selected before any servlet with an extension mapping (like *.html), while a servlet with mapping / will be selected only after extension mappings are considered (and will be used for any request which doesn't match anything else---it is the "default servlet").
In particular, a /* mapping will always be selected before a / mapping. Having either prevents any requests from reaching the container's own default servlet.
Either will be selected only after servlet mappings which are exact matches (like /foo/bar) and those which are path mappings longer than /* (like /foo/*). Note that the empty string mapping is an exact match for the context root (http://host:port/context/).
See Chapter 12 of the Java Servlet Specification, available in version 3.1 at http://download.oracle.com/otndocs/jcp/servlet-3_1-fr-eval-spec/index.html.

Where does getRequestDispatcher("path") look?

Using embedded tomcat, this code:
System.out.println("getServletPath: " + request.getServletPath());
System.out.println("getServletContext: " + request.getServletContext().getContextPath());
System.out.println("getServerName: " + request.getServerName());
System.out.println("getServerPort: " + request.getServerPort());
Prints out:
getServletPath: /example
getServletContext:
getServerName: localhost
getServerPort: 9090
Does that mean that:
request.getRequestDispatcher("/example/read.jsp").forward(request, response);
Will look at this URL to forward(request, response) to the JSP:
http://localhost:9090/example/read.jsp?
Is there a way to print out what absolute URL getRequestDispatcher("relativePath") is addressing?
The Servlet Specification explains this
The getRequestDispatcher method takes a String argument describing a
path within the scope of the ServletContext. This path must be
relative to the root of the ServletContext and begin with a ‘/’, or
be empty. The method uses the path to look up a servlet, using the
servlet path matching rules in Chapter 12, “Mapping Requests to
Servlets”, wraps it with a RequestDispatcher object, and returns the
resulting object. If no servlet can be resolved based on the given
path, a RequestDispatcher is provided that returns the content for
that path.
Those rules are the following
The container will try to find an exact match of the path of the request to the path of the servlet. A successful match selects the
servlet.
The container will recursively try to match the longest path-prefix. This is done by stepping down the path tree a directory
at a time, using the ’/’ character as a path separator. The longest
match determines the servlet selected.
If the last segment in the URL path contains an extension (e.g. .jsp), the servlet container will try to match a servlet that handles
requests for the extension. An extension is defined as the part of
the last segment after the last ’.’ character.
If neither of the previous three rules result in a servlet match, the container will attempt to serve content appropriate for the
resource requested. If a "default" servlet is defined for the
application, it will be used. Many containers provide an implicit
default servlet for serving content.
You ask
Does that mean that:
request.getRequestDispatcher("/example/display.jsp").forward(request,
response); Will look at this URL to forward(request, response) to the
JSP:
http://localhost:9090/example/display.jsp?
No, it doesn't send an HTTP request, so the path has nothing to do with a URI. It's more of an internal path that the Servlet container will try to match with its various url-mappings for Servlets.
You also ask
Is there a way to print out what absolute URL getRequestDispatcher("relativePath") is addressing?
No. And it isn't exactly an absolute URL. It's a path that can be handled by some resource in the web application context.
After your edit, you addWebapp to your Tomcat instance.
tomcat.addWebapp(null, "/view2/example2", new File("src/com/example/view/example").getAbsolutePath());
You then send a request to
/view2/example2/read.jsp
I'm going to assume that read.jsp is in
src/com/example/view/example/
I believe it's in the publicly accessible part of the web application and therefore the Servlet container can render it and respond with it.
You've also added a webapp with addContext which seems to be similar to addWebapp
context = tomcat.addContext("", base.getAbsolutePath());
and added servlet mappings to this context.
Tomcat.addServlet(context, "example", new ExampleController());
context.addServletMapping("/example/*", "example");
I was wrong about the /example/* not being able to handle /example.
When you send a request to
/example
since the context path is "", the Context above will be used and the mapping will match the ExampleController registered above. Your Servlet code will execute and reach
request.getRequestDispatcher("/view2/example2/read.jsp").forward(request, response);
Note the javadoc of ServletRequest#getRequestDispatcher(String)
The pathname specified may be relative, although it cannot extend
outside the current servlet context.
In other words, this Servlet, ExampleController was registered in the ServletContext mapped to the context path "", ie. root. The path /view2/example2/read.jsp is referring to another context. Since this context doesn't have a mapping for it, it responds with 404.
You can get a reference to another web applications in a different context. You have to use ServletContext#getContext(String). For example
ServletContext otherContext = request.getServletContext().getContext("/view2/example2");
Now that you have the ServletContext, you can get a RequestDispatcher for a resource in that context.
otherContext.getRequestDispatcher("/read.jsp").forward(request, response);
since ServletContext#getRequestDispatcher(String) states
The pathname must begin with a / and is interpreted as relative to the current context root.
Final Answer:
getRequestDispatcher("path") will look at the directory set in the addWebapp method when referencing a JSP file. If a blank page or NullPointerException is displayed, ensure that you have done the following:
Remove all the addWebapp definitions.
Run addContext then addWebApp like this so they both point to ROOT:
File base = new File("src/com/example/view");
context = tomcat.addContext("", base.getAbsolutePath());
tomcat.addWebapp(null, "/", base.getAbsolutePath());
In the servlet point to the jsp using request.getRequestDispatcher("/example/read.jsp").forward(request, response); provided that the directory /example exists in "src/com/example/view".

Configuring Spring to directory structure rather than *.html or other pattern

I want my spring application to access url like http://myapplication.com/app1/feature/ rather than http://myapplication.com/app1/feature.html
So basically how should I configure.
My Current config is below it works with *.html
web.xml
and it gives 404 error it I do map it to * .* etc
and it gives 404 error it I do map it to * .* etc
The servlet spec is very strict about what's allowed in a url-pattern. You're probably looking for /*, which will direct all requests to the DispatcherServlet, regardless of the path. Then it's up to you to define what happens.
For reference, "Specification of Mappings" (12.2) allows the following patterns:
A string beginning with a ‘/’ character and ending with a ‘/*’ suffix is used for
path mapping.
A string beginning with a ‘*.’ prefix is used as an extension mapping.
The empty string ("") is a special URL pattern that exactly maps to the
application's context root, i.e., requests of the form http://host:port/<contextroot>/. In this case the path info is ’/’ and the servlet path and context path is
empty string (““).
A string containing only the ’/’ character indicates the "default" servlet of the
application. In this case the servlet path is the request URI minus the context path
and the path info is null.
All other strings are used for exact matches only

Trouble with url-pattern in web.xml

My filter mapping in web.xml is as follows:
<filter>
<filter-name>LoginCheckFilter</filter-name>
<filter-class>com.tutorial.filter.LoginCheckFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LoginCheckFilter</filter-name>
<url-pattern>/admin*</url-pattern>
</filter-mapping>
When I run my app and hit http://localhost:8080/admin my filter is not getting executed. I'm not able to understand why.
Is there any problem with the pattern. Also If I remove '*' from the pattern then the filter is getting executed on hitting above url.
Need help on this. Thanks.
The specifications (paragraph 12.2) says the following:
In the Web application deployment descriptor, the following syntax is
used to define mappings:
A string beginning with a ‘/’ character and
ending with a ‘/*’ suffix is used for path mapping.
A string
beginning with a ‘*.’ prefix is used as an extension mapping.
The
empty string ("") is a special URL pattern that exactly maps to the
application's context root, i.e., requests of the form
http://host:port/<contextroot>/. In this case the path info is ’/’ and
the servlet path and context path is empty string (““).
A string
containing only the ’/’ character indicates the "default" servlet of
the application. In this case the servlet path is the request URI
minus the context path and the path info is null.
All other strings
are used for exact matches only.
So the * is taken literally, unless the pattern ends with /* or starts with *.
Use /admin/* instead of /admin*.
Servlet container will not recognize /admin* as correct URL pattern.
Maybe try:
<url-pattern>/admin/*</url-pattern>

Categories

Resources