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.
Related
/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.
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.
I've got: trivial Spring MVC sample app. I configure DispatcherServlet to handle all requests which start with dispatcher/.
Problem: After deploying to Tomcat, if I try to go to localhost:8080 page I get The requested resource (/) is not available.. But I can go through localhost:8080/dispatcher path.
Questions:
Why does simple test works if we do mockMvc.perform(get("/")) instead of mockMvc.perform(get("/dispatcher"))?
Code:
web.xml:
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/dispatcher/*</url-pattern>
</servlet-mapping>
Controller:
#Controller
#RequestMapping("/")
public class HelloController {
#RequestMapping(method = RequestMethod.GET)
public String printWelcome(ModelMap model) {
model.addAttribute("message", "Hello world!");
return "hello";
}
}
Test:
#Test
public void simple() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(model().attribute("message", "Hello world!"))
.andExpect(view().name("hello"));
}
Problem: After deploying to Tomcat, if I try to go to localhost:8080
page I get The requested resource (/) is not available.. But I can go
through localhost:8080/dispatcher path.
Accessing your controller from the Servlet Container's perspective - what you're doing when you type localhost:8080/dispatcher - requires a lot of information in the url path. For example, your HelloController is
configured to be handle by the mvc-dispatcher Servlet
mvc-dispatcher Servlet is declared in a web application (defined by your web.xml)
there could be multiple Servlets in your web application
the web application is deployed to your Servlet container (Tomcat)
there could be multiple web applications in the Servlet Container
There's lots of ambiguity in resolving a url when you take all this into consideration. As #san krish pointed out, and assuming you've deployed your application to Tomcat's root context, you can force everything through mvc-dispatcher and map it to /* as default, and end up serving your HelloController from localhost:8080/.
Why does simple test works if we do mockMvc.perform(get("/")) instead
of mockMvc.perform(get("/dispatcher"))?
Accessing your controller from mockMvc in your test is pretty localized. Depending on how your test was set up, a MockMvc instance was created either from:
MockMvcBuilders.standaloneSetup(controller) - here your controller is passed directly to the test for testing, therefore MockMvc knows nothing about a mvc-dispatcher and it's "/dispatcher" path.
MockMvcBuilders.webAppContextSetup(webApplicationContext) - here, the test class is annotated with a #ContextConfiguration that points to your "mvc-dispatcher" xml or #Configuration class. In this situation, MockMvc would be acting on everything localized under "mvc-dispatcher" so there's no need to reference the "dispatcher" in the path.
In the web.xml map the wildcard entry
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
It will pass all in-coming requests to the dispatcher servlet
<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.
At first in my web server I only had one REST servlet. Something like:
#Path("/")
public class Controller {
#GET
#Produces({ MediaType.TEXT_HTML })
public Response get(#Context UriInfo info) throws Exception {
...
}
#GET
#Path("resource1")
#Produces({ MediaType.TEXT_HTML })
public Response resource1() throws Exception {
...
}
...
}
And the web.xml:
<servlet>
<servlet-name>rest</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>xpto.mypack1;xpto.mypack2</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>rest</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
But then I wanted to add some static html to the server, so I updated the servlet mapping to
/rest/*
and the #Path directive of Controller servlet class from "/" to "/rest". Everything works fine but the sub-resources or methods of controller with the #path directive that stopped working.. ie:
/ works fine since I have an index.html page at root
/rest works fine, it invokes the get method of my servlet
/rest/resource1 returns 404 http code...
Any help? I already tried a list of combinations of / after and before each #Path directive, with no success... Many thanks
One update:
I used the trace util and got the following results:
for /[app-name]/rest (it works):
X-Jersey-Trace-002 accept right hand path java.util.regex.Matcher[pattern=/rest(/.*)? region=0,11 lastmatch=/rest]: "/rest" -> "/rest" : ""
X-Jersey-Trace-003 accept resource: "rest" -> #Path("/rest") xpto.mypack.Controller
X-Jersey-Trace-000 accept root resource classes: "/rest"
X-Jersey-Trace-001 match path "/rest" -> "/application.wadl(/.)?", "/rest(/.)?"
for /[app-name]/rest/resource1 (it doesn't work):
X-Jersey-Trace-002 matched exception mapper: com.sun.jersey.api.NotFoundException#4fd41dc3 -> xpto.myclass
X-Jersey-Trace-003 mapped exception to response: com.sun.jersey.api.NotFoundException#4fd41dc3 -> 404 (Not Found)
X-Jersey-Trace-000 accept root resource classes: "/resource1"
X-Jersey-Trace-001 match path "/resource1" -> "/application.wadl(/.)?", "/rest(/.)?"
I hope it helps someone to help me..
If you define your servlet mapping as /rest/*, don't repeat /rest in the #Path annotation of your resources. I.e. all you need to do is keep the controller as is (in your question above) and just change the servlet mapping. The URL at which the resources are available is:
<application_context_path>/<servlet_mapping>
So, if you change the #Path annotation from #Path("/") to #Path("rest") and you also change the servlet mapping to /rest, then your resources would be available at:
<application_context_path>/rest/rest/*