How to handle HTTP OPTIONS with Spring MVC? - java

I'd like to intercept the OPTIONS request with my controller using Spring MVC, but it is catched by the DispatcherServlet. How can I manage that?

I added some more detail to the Bozho answer for beginners.
Sometimes it is useful to let the Spring Controller manage the OPTIONS request (for example to set the correct "Access-Control-Allow-*" header to serve an AJAX call).
However, if you try the common practice
#Controller
public class MyController {
#RequestMapping(method = RequestMethod.OPTIONS, value="/**")
public void manageOptions(HttpServletResponse response)
{
//do things
}
}
It won't work since the DispatcherServlet will intercept the client's OPTIONS request.
The workaround is very simple:
You have to... configure the DispatcherServlet from your web.xml file as follows:
...
<servlet>
<servlet-name>yourServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>dispatchOptionsRequest</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
...
Adding the "dispatchOptionsRequest" parameter and setting it to true.
Now the DispatcherServlet will delegate the OPTIONS handling to your controller and the manageOption() method will execute.
Hope this helps.
PS. to be honest, I see that the DispatcherServlet append the list of allowed method to the response. In my case this wasn't important and I let the thing go. Maybe further examinations are needed.

#RequestMapping(value="/youroptions", method=RequestMethod.OPTIONS)
public View getOptions() {
}
You should configure the dispatcherServlet by setting its dispatchOptionsRequest to true

As a quick supplement to the above 2 answers, here's how to enable dispatchOptionsRequest in a servlet 3 (no web.xml) environment as it took me a while to work out how to apply the answers above in a non-xml setup.
In a spring 3.2 / servlet 3 environment, you will have some variety of DispatcherServlet initializer class which is the java equivalent of web.xml; in my case it's the AbstractAnnotationConfigDispatcherServletInitializer. Adding the following code will enable dispatchOptionsRequest:
#Override
protected void customizeRegistration(Dynamic registration) {
registration.setInitParameter("dispatchOptionsRequest", "true");
}

I took the following approach:
Using Maven (or manually) pull in this dependancy:
<dependency>
<groupId>com.thetransactioncompany</groupId>
<artifactId>cors-filter</artifactId>
<version>1.3.2</version>
</dependency>
This has an implementation to capture all the inbound OPTIONS requests. Into the web.xml file add the following config:
<filter>
<filter-name>CORS</filter-name>
<filter-class>com.thetransactioncompany.cors.CORSFilter</filter-class>
<init-param>
<param-name>cors.supportedHeaders</param-name>
<param-value>Content-Type,Accept,Origin</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CORS</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
The problem I've seen with the /** approach is a more specific Controller implementation will override this.

For Spring without web.xml file, and based on Paul Adamson answer, I just set the parameter dispatchOptionsRequest to true into the dispatcher, to process the Options method calls.
ServletRegistration.Dynamic dispatcher = container.addServlet("dispatcher", new DispatcherServlet(applicationContext));
dispatcher.setInitParameter("dispatchOptionsRequest", "true");
dispatcher.setLoadOnStartup(1);
dispatcher.addMapping("/*");

Related

In Spring MVC, what is even the purpose of web.xml?

In Spring MVC, people typically build a Dispatcher Servlet that controls the other Servlets. The pipeline includes a request to web.xml which is then routed to a dispatcher of class org.springframework.web.servlet.DispatcherServlet. The URL pattern can be / or *.htm* to ensure that all requests go there.
The question is: in this pattern, what is even the purpose of web.xml? One would think that it is just useless overhead. I mean, if you're not going to use another dispatcher... or are you?
Basically in a regular Java app context will be fetched in some self-created main method, means main method is your starting point. Application will run from the main and will go other methods after.
public class FooClass{
public static void main(String[] args) {
//some code
}
But in the Spring web app,
the starting point is actually web.xml. It start from here, then flow goes to the other defined classes and methods
For example, when you write these codes, you basically give the order to the web application that you should start from here
Kind of you define your starting point. Think that it is main method in normal Java
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-mvc-validation-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
And in second part you give order to dispatcher that start from here. It means you give a url-pattern -starting point. You can give anything in here but "/" this is the common use
<!-- Step 2: Set up URL mapping for Spring MVC Dispatcher Servlet -->
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
I hope it is clear. Else ask for more explanations.

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 4.1.4 and Jackson 2.5, annotation driven setup not converting REST responses to JSON

I coming back to Spring after spending 5 years doing other things. I have an initial projects which is designed to provide a HTTP REST service that returns JSON.
My problem is that I cannot get the service to convert the response to JSON. Instead I get errors like this:
javax.servlet.ServletException: Circular view path [hello]: would dispatch back to the current handler URL [/hello] again. Check your ViewResolver setup! (Hint: This may be the result of an unspecified view, due to default view name generation.)
at org.springframework.web.servlet.view.InternalResourceView.prepareForRendering(InternalResourceView.java:205) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.view.InternalResourceView.renderMergedOutputModel(InternalResourceView.java:145) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:303) ~[spring-webmvc-4.1.4.RELEASE.jar:4.1.4.RELEASE]
...
My web.xml looks like this:
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<context-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>au.com.abc.service</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>fxServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>
org.springframework.web.context.support.AnnotationConfigWebApplicationContext
</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>au.com.abc.controller</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
And my Controller class looks like this:
#RestController
public class FXRESTController {
#RequestMapping(value = "/hello")
public Map<String,Object> rootContextHandler() {
Map<String,Object> data = new HashMap<>();
data.put("X", "abc");
return data;
}
}
It really cannot get any simpler. I've been passing the request header Accept='application/json', but it's still not working. I have had this type of thing working in the past, but I don't have that code anymore. I can also see this in the logs:
... Invoking request handler method: public java.util.Map au.com.abc.controller.FXRESTController.rootContextHandler()
... Service responding
... Invoking afterPropertiesSet() on bean with name 'hello'
... Rendering view [org.springframework.web.servlet.view.JstlView: name 'hello'; URL [hello]] in DispatcherServlet with name 'fxServlet'
... Added model object 'X' of type [java.lang.String] to request in view with name 'hello'
... Error rendering view [org.springframework.web.servlet.view.JstlView: name 'hello'; URL [hello]] in DispatcherServlet with name 'fxServlet'
Which suggests to me that it's trying to render a JSTL view. Why - I don't know considering I've asked for JSON.
Any ideas what I've done wrong?
I've read a ton of blogs and so far I cannot see any differences between what they have done and what I've done.
Oh and here are my gradle resolved dependencies:
You only have a controller, that doesn't do anything for enabling JSON.
You have to have a #Configuration annotated class which is also annotated with #EnableWebMvc to have automatic JSON conversion enabled. See also this section of the reference guide.
#Configuration
#EnableWebMvc
public class WebConfiguration {}
You are running with the DispatcherServlet defaults which are very basic.
Use #ResponseBody.
ALso donot direct all request to spring
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
ALways better to add a layer like this:
<servlet-mapping>
<servlet-name>fxServlet</servlet-name>
<url-pattern>/webapp/*</url-pattern>
</servlet-mapping>
FOr a given project only following request will be directed to spring
http://localhost:8080/Proj/app/hello
Other request can be handled different..thus you can handle request in a different way and not dependent on spring..like
http://localhost:8080/Proj/servlethandler
The above request will not go to spring and can be intercepted by a servlet etc..

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 initialize Java EE 5 JAX-WS 2.0 Web Service with Parameters

Application configuration:
Web application using java first method of creating JAX-WS 2.0 Web Services with annotations.
WebLogic 10.3
My Requirements
The requirements I have are to deploy a single web service implementation class, but change logic based on the URL from which the service was accessed.
Question:
I'm assuming a good way to do this is to deploy different mappings in web.xml and initialize them with different parameters. Is there a better way?
What is the best way to switch logic off the URL from which the web service was accessed? Should I try to configure two servlet mappings in web.xml with initialization parameters (tried, but couldn't get it to work), or should I parse the URL in the service impl? Any other alternatives?
What I've Tried (but didn't work)
I have tried adding the <init-param> in the <servlet> element in web.xml. However, can't get to the ServletConfig object inside the web service to retrieve the param. The web service does not have all the functionality of a standard Servlet (even if I implement Servlet or ServletContextListener). I only have access to the WebServiceContext (it seems) and from there I can only get <context-param> elements--but I would need <init-param> elements instead.
In web.xml, I enter two <servlet> elements using the same Java class, but which map to two different URLs as follows. Notice how the "source" param is different in each Servlet mapping.
<servlet>
<servlet-name>Foo</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>1</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Foo</servlet-name>
<url-pattern>/Foo</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>Bar</servlet-name>
<servlet-class>com.Foo</servlet-class>
<init-param>
<param-name>source</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Bar</servlet-name>
<url-pattern>/Bar</url-pattern>
</servlet-mapping>
You very well may have, but did you try using MessageContext at runtime to determine what the source is?
#WebService
public class CalculatorService implements Calculator
{
#Resource
private WebServiceContext context;
#WebMethod
public void getCounter()
{
MessageContext mc = wsContext.getMessageContext();
// you can grab the HttpSession
HttpSession session = (HttpServletRequest)mc.get(MessageContext.SERVLET_REQUEST)).getSession();
// ...or maybe the path info is enough
String path = mc.get(MessageContext.PATH_INFO);
// the query itself should almost definitely be enough
String query = (String) mc.get(MessageContext.QUERY_STRING);
}
}
I got the idea from http://sirinsevinc.wordpress.com/category/jaxws/. Haven't tried it, though.

Categories