SpringMVC conflict by #PathVariable and resources - spring-mvc

I have a problem while use Spring MVC:
I wrote a Controller, a method named
#RequestMapping("/{moduleName}/{subModuleName}")
public ModelAndView getModuleContent(#PathVariable String moduleName, #PathVariable String subModuleName) {
...},
now the problem is, when I access a static resource, for example:
http://www.mytomcat:8080/images/testpng
it would mapping that controller, but I don't want this, how can I resolve the conflict?

Try to specify regex pattern for path vars:
#RequestMapping(value = "/{year:\\d+}/{month:\\d+}/**", method = RequestMethod.GET)
This helps spring to avoid conflicts with <mvc:resources/> mapped to path like /images/**. If your path variable can contain all except 'images' you can try regex like this: (?!images).

configure following accordingly.
<mvc:resources mapping="/resources/**" location="/public-resources/"/>

You may configure resource handlers for resource folders such as /images.
It should help, because mapping of these handlers would be more specific than /{moduleName}/{subModuleName}, therefore resource handlers should take precedence.

Related

Some questions about Apache Shiro in Spring MVC

I downloaded a Spring MVC project which using Apache Shiro for security layer. In the controller, it uses #RequiresPermissions to define the permission, for example:
#RequiresPermissions("sys:user:view")
#RequestMapping(value = {"index"})
public String index(User user, Model model) {
return "modules/sys/userIndex";
}
#RequiresPermissions("sys:user:view")
#RequestMapping(value = {"list", ""})
public String list(User user, HttpServletRequest request, HttpServletResponse response, Model model) {
return "modules/sys/userList";
}
I have couple of questions about this:
What kind of permission is this? I checked the Shiro documents, based on the doc, three parts should be "domain:action:instance", but in the code above, the first two parts are path, and the last part is the action. So I'm just confused.
I'm not sure whether the annotation #RequiresPermissions is using to define the permission. I tried to use that define a new permission, but failed. If it's not, how to define a new permission?
The actual permission String is freeform. "domain:action:instance" is an example. You could use something like users:write:1234 or just more general users:write. But there is nothing stopping you from using something like <domain>:<instance>:<action>. Using the same two examples you would have users:1234:write and users:*:write (respectively).
As for #2 your realm (or a RolePermissionResolver) is responsible for defining the mapping between users and permissions (or roles and permissions)

Simplify ResourceHandlerRegistry.addResourceHandler(..)?

Every time I want to use ResourceHandlerRegistry.addResourceHandler(..) to tell Spring a certain directory is resource, I need to specify a path for the handler and a path for resource location, for instance.
registry.addResourceHandler("/javascript/**").addResourceLocations("/javascript/");
registry.addResourceHandler("/html/**").addResourceLocations("/html/");
This gets very repetitive when I have more than one resource directory. Is it possible to tell Spring MVC, "/html" or "/javascript" are resource directories?
Every directory is a directory in spring.
To exclusively specify resource folder you have to have this configuration.
Otherwise for all resource requests spring will start finding out controller mapping.
Found the answer from this other StackOverflow question https://stackoverflow.com/a/31349904/1772825
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}

Spring MVC RequestMapping confusion

I'm having trouble with Spring MVC RequestMapping and I'm hoping someone can give me advice on how to proceed.
I'm working on a server that will be accessed by a legacy client. The client sends simple requests (see below) but cannot be changed in any way to accommodate the new server.
The client sends gets or posts of the form /dbintf?command=GETINFO,acctNum=111
The server is called dbintf and it returns a simple text response.
I would like to create a Spring controller for each command and annotate a single controller method with
a params mapping that is based on the command parameter. So the mapping in the GETINFOController would be:
#RequestMapping(params="command=GETINFO")
public String serviceRequest(....
However, I cannot get this to work. Here are some details of the service.
The servlet mapping in web.xml:
<servlet-mapping>
<servlet-name>dbintf</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
The annotation scanning directives in intf-servlet.xml
<context:component-scan base-package="com.intf.controller" />
<bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/>
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>
I have been trying various combinations of things to try to get this scheme to work and I'll boil this
trial-and-error work down to two examples:
This does not work. The response is: "The requested resource () is not available."
#RequestMapping( params="command=GETINFO")
public class TESTController {
#RequestMapping( method = RequestMethod.GET)
public String serviceRequest
If I change the url to add another level: /dbintf/test?command=GETINFO,acctNum=111
it works if I add a URL pattern to the handler method annotation.
#RequestMapping( params="command=GETINFO")
public class TESTController {
#RequestMapping( value="/test",method = RequestMethod.GET)
public String serviceRequest
This makes me think that params mapping alone will not work. It appears that a URL
pattern must be part of any mapping scheme. For my case however, there is no
URL pattern available since the the client request URL is fixed as: dbintf?.....
My question: is it possible to use parameter based mapping alone and not
in conjunction with a pattern? If so, what am I missing?
Thanks in advance for any help or advice,
beeky
I don't see in your config where you are mapping your controllers to "/dbintf"? You've got your DispatcherServlet (assuming that "dbintf" servlet is your DispatcherServlet) mapped to just "/", not "/dbintf". Are you deploying your webapp so that it's available at "/dbintf" relative to your container's "/"? I guess what I'm trying to get at is that you may just need to add some config to your #RequestMapping's:
#RequestMapping(value="/dbintf", params="command=GETINFO")

How do I get Web.xml context-param values in controller action method?

This feels like a basic question, but I haven't had much luck Googling.
My app connects to an SMTP server and sends mail through it. I need this SMTP server to be configurable based on which environment the app is deployed to.
How can I specify the specify the SMTP server name in my web.xml config file and access it from my Spring MVC 3.0 controller?
The controller does not extend or implement anything. It is completely annotation driven with #Controller and #RequestMapping. From what I have seen online, people access context-params via the servlet API. Being annotation driven, I do not have access to the servlet object.
I solved this.
Make your controller implement ServletContextAware, which requires a method called
setServletContext(ServletContext servletContext)
Spring MVC will inject the servlet context into this method if your controller is ServletContextAware.
Create a private variable on your controller to store the servletController that is injected into the above method. You can now use servletContext just as you would if you were using a regular servlet.
hth.
Adding an instance of Servletcontext and autowiring it worked for me
#Controller
public MyController {
// other instances relevant to your requirement
#Autowired
private ServletContext sCtx;
//other methods relevant to your requirement
}
I suppose following also should work:
void action(final HttpServletRequest request) {
final paramValue = request.getSession().getServletContext().getInitParameter("paramName");
...
}

Difference between / and /* in servlet mapping url pattern

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.

Resources