Trouble with servlet url-pattern matching with wildcards - java

I am having difficulty getting requests mapped to the correct servlet when the servlet-mapping url-pattern uses a wildcard. I want all requests that begin with "/profile-api" to be mapped to a new REST service I'll be writing soon.
From web.xml:
<!-- default servlet -->
<servlet-mapping>
<servlet-name>professional</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- profile api -->
<servlet-mapping>
<servlet-name>profile-api</servlet-name>
<url-pattern>/profile-api/*</url-pattern>
</servlet-mapping>
Pseudo-code from the controller that allows a third-party system to update a user's "email communication opt-in status" (OptinResponse is a domain class that will ultimately be transformed to JSON and returned to caller):
#Controller
#RequestMapping("/profile-api")
public class ProfileAPIController {
#RequestMapping(method = RequestMethod.GET, value="/setoptin/{userID}")
public #ResponseBody OptinResponse setOptinStatus(#PathVariable String userID) {
return new OptinResponse("200", "Successfully set optin status for user: " + userID);
}
}
I would expect a request to "{localhost}/profile-api/setoptin/12345" to be correctly routed to the ProfileAPIController, but it is not.
Changing the servlet-mapping url-pattern to be more specific but still generic also fails:
<servlet-mapping>
<servlet-name>profile-api</servlet-name>
<url-pattern>/profile-api/setoptin/*</url-pattern>
</servlet-mapping>
The ONLY way I have been able to get my request routed as intended is to include the full, exact path:
<servlet-mapping>
<servlet-name>profile-api</servlet-name>
<url-pattern>/profile-api/setoptin/12345</url-pattern>
</servlet-mapping>
Obviously, that's unacceptable, as the user id must be variable.
In all cases, the request is instead mapped to the default "professional" servlet. I have tried reordering the servlet-mapping nodes to no avail. I have "alwaysUseFullPath" set to "true" in the AnnotationMethodHandlerAdapter bean in the servlet config (but have tried it as "false", too). I feel as though I'm overlooking something simple, but can't see the forest for the trees.

Related

Struts to spring migration getting 404 error

/spring/fetchAllUsers URL which am trying
web.xml
<servlet>
<servlet-name>user</servlet-name>
<servlet-class> org.springframework.web.servlet.DispatcherServlet </servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springContext/dataSource.xml</param-value>
</init-param>
<load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>user</servlet-name>
<url-pattern>/spring/</url-pattern>
</servlet-mapping>
Controller Code
#RequestMapping(value = "/getAllUsers", method = RequestMethod.POST)
#ResponseBody
#Transactional(propagation=Propogation.REQUIRED,rollBackFor=Exception.class)
public String fetchAllUsers(){
setInputStream(userDelegate.fetchAllUsers());
return success;
Details:
And I have mvc annotation driven and mvc default servlet handler in user-servlet.xml
Getting 404 error code when try to access this URl when doing migration from struts to spring
Break point is not hit when this URL is hit and no errors in console as well.Please suggest on the above issue
According to your servlet mapping only one url is allowed localhost:8080/context/spring/ that is not mapped with your controller.
When we defined a servlet-mapping, we are using SimpleUrlHandlerMapping. To understand servlet url mapping let define a servlet mapping :
<servlet-mapping>
<servlet-name>user</servlet-name>
<url-pattern>/spring/*</url-pattern>
</servlet-mapping>
Now the handler will actually use the * part to find the controller. It will not search /spring/createUser, it will only search for /createUser to find a corresponding Controller.
#RequestMapping("/createUser")
In your case You need to either change your url to localhost:8080/spring/spring/createUser or remove prefix from Controller #RequestingMapping(/createUser).
since your servlet url mapping has already include /spring you don't need to include it in #RequestMapping.
try this:
#RequestMapping(value = "/createUser/", method = RequestMethod.POST)
Spring interprets urls /spring/createUser/ and /spring/createUser differently(atleast in POST methods that i just tested).
Change your #RequestMapping url to /spring/createUser.
Also mind that the url you call is /spring/createUser without a trailing slash("/").
Your method
#RequestMapping(value = "/spring/createUser", method = RequestMethod.POST)
Hope this helps.

Spring mvc servlet url does not map correctly

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>

Spring MVC Can't configure Dispatcher Servlet right

I'm having trouble configuring the Dispatcher for Spring. What I am trying to achieve is:
Build REST WebService to receive requests
Have HTML + Ajax pages consuming the data (Therefore, I don't have Views in my Spring project)
So far I have only 2 HTML pages: Login (using j_security_check) and Main page. Both very simple. I also have a simple controller:
MainController.java
#RestController //Or #Controller and #ResponseBody, no difference, right?
public class MainController {
#RequestMapping("rest/main/data")
public String getData () {
return "{data: \"DATA HUEHUE\"}"; // Yes, I'm brazilian
}
}
And I have tried the following configuration for web.xml and dispatcher-servlet.xml:
web.xml:
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
dispatcher-servlet.xml
<context:component-scan base-package="com.example.controller"/>
This doesn't work. I get the message INFO: Mapped URL path [/rest/main/data] onto handler 'mainController' but when I try to access I get No mapping found for HTTP request with URI [/myapp/rest/main/data] in DispatcherServlet with name 'dispatcher'
I also have tried:
On web.xml: <url-pattern>/</url-pattern>
On dispatcher-servlet: The same
What happened: The controller DID work but the application also tried to map my login.html and couldnt find a match so I got 404 ;-;
I'm aware of that "standard" configuration using a prefix and a sufix, but since I dont have views here I dont think that's the right approach.
I'm kinda new at Spring (as you may have noticed), so please be gentle on the answers.
Any ideas?
Thanks in advance :)
My project tree:
-project
--src
---main
----webapp
-----WEB-INF
------web.xml
------weblogic.xml
------dispatcher-servlet.xml
-----www
------main.html
-----login.html
(Login is outside www)
With the first approach if you modify the controller code to have /rest/main/data It will work.
#RestController //Or #Controller and #ResponseBody, no difference, right?
public class MainController {
#RequestMapping("/rest/main/data")
public String getData () {
return "{data: \"DATA HUEHUE\"}"; // Yes, I'm brazilian
}
}
What is happening in happening in the second approach is that since you have Spring Security configured you need to be authenticated first but for that it finds the Login.html and can not find it. This may be because of incorrect configuration.

#PathVariable is not working in spring

I have PersonController as below :
#Controller
#RequestMapping("person")
public class PersonController {
#RequestMapping(value= "/{personId}", method = RequestMethod.GET, produces={"application/json"})
public #ResponseBody Map<String, Object> getPerson(#PathVariable("personId") Integer personId) {
// code to get person
}
Tomcat starts up fine, I see this in the console :
Mapped "{[/person/{personId}],methods=[GET],params=[],headers=[] ,consumes=[],produces=[application/json],custom=[]}" onto public java.util.Map<java.lang.String, java.lang.Object> com.test.web.controller.PersonController.getPerson(java.lang.Integer)
But if I hit the url http://localhost:8080/sample/person/1 I get
HTTP Status 404 - /sample/person/1
In the web.xml
<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/servlet-context.xml</param-value>
</init-param-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/sample/*</url-pattern>
</servlet-mapping>
I copy/pasted your PersonController class and it worked fine here.
So I did check your web.xml and your app servlet is mapping the pattern "/sample/*".
If I am corret, I suspect your project is called "sample" in Eclipse. In that case, you have to access your site as follows:
http://localhost:8080/sample/sample/person/1
The mapping in your web.xml will always start from your root context, and that is why you are getting 404 error.
If you want to access your controller from the root domain (in this case it is your actual Eclipse project name by default, but it can be configured too) you can use your servlet mapping as follows:
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
I recommend that you use /rest/* or other mark since it will scale better for other types of content.
Let me know if it worked.

How to allow access to static content when having default servlet

I map all requests to /* to a specific servlet.
My static content is hidden by this configuration.
How can i allow access to specific files (such as crossdomain.xml)?
When you map /* to a specific servlet, all requests will be forwarded to that servlet, unless you provide a more explicit mapping to another servlet.
That is, if you have /* mapped to ServletA, and /static/* mapped to ServletB, then following Servlets will get called.
http://localhost:8080/abc.jpg -> ServletA
http://localhost:8080/static/abc.jpg -> ServletB
http://localhost:8080/xyz/abc.jpg -> ServletA
So one option you have is to write a Servlet to handle the static content, which will grab the file and return it as response. You can map that servlet to a prefixed by something like /static/*. This requires that all URL references to your static files to be updated to contain this '/static' part.
If that is not feasible for you, then probably you can use the same servlet, but mapped to multiple URL patterns (probably by extension) as follows.
<servlet>
<servlet-name>static-servlet</servlet-name>
<servlet-class>xxx.yyy.StaticServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>static-servlet</servlet-name>
<url-pattern>*.jpg</url-pattern>
</servlet-mapping>
If you want this to be fine-grained to the level of each file, you can map the servlet to your file URL as well.
Cookbook:
Map your controller Servlet on a more specific url-pattern like /app/*.
Put all the static content in a specific folder like /static.
Create a Filter which is mapped on /* which transparently continues the chain for any /static requests and dispatches other requests to /app.
So, in a nutshell:
<filter>
<filter-name>filter</filter-name>
<filter-class>com.example.Filter</filter-class>
</filter>
<filter-mapping>
<filter-name>filter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet>
<servlet-name>controller</servlet-name>
<servlet-class>com.example.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>controller</servlet-name>
<url-pattern>/pages/*</url-pattern>
</servlet-mapping>
with the following in filter's doFilter():
String uri = ((HttpServletRequest) request).getRequestURI();
if (uri.startsWith("/static/")) {
chain.doFilter(request, response); // Goes to default servlet.
} else {
request.getRequestDispatcher("/app" + uri).forward(request, response);
}
No, you do not end up with extra /app path in the URL. It's fully transparent. Make if necessary "/static" and/or "/app" an <init-param> of the filter.
And one more(a direct) servlet mapping like this<servlet-mapping><servlet-name>StaticContentServlet</servlet-name><url-pattern>/crossdomain.xml</url-pattern></servlet-mapping>
probably you can put your static content under different URL like /static/* and then map this URL to a Servlet which responds with the static content.

Categories

Resources