I want to build a REST-API with Jax-Rs with cors enabled. So I googled how to do and found this:
http://www.developerscrappad.com/1781/java/java-ee/rest-jax-rs/java-ee-7-jax-rs-2-0-cors-on-rest-how-to-make-rest-apis-accessible-from-a-different-domain/
The solution is a ResponseFilter, that adds some header information to every response, so that the browser of the user knows, that cross domain accesses are allowed.
Because the ResponseFilter is not executed when I do any request (tried GET, POST and OPTIONS), I googled again and found this:
ContainerResponseFilter not working
#lefloh gave a good answer, that sounds logic (to remove the annotation #PreMatching). It did so, but nevertheless my filter is not invoked, when I do a Http-Request.
This is my code:
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
import java.io.IOException;
#Provider
public class RESTServiceResponseFilter implements ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext parContainerRequestContext, ContainerResponseContext parContainerResponseContext) throws IOException {
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Origin", "*" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Credentials", "true" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Methods", "GET, POST, DELETE, PUT" );
parContainerResponseContext.getHeaders().add( "Access-Control-Allow-Headers", "Content-Type" );
}
}
I kept on googling and found out, that I forgot to add the Filter to the web.xml. So I also did that:
<servlet>
<display-name>webinterface.api</display-name>
<servlet-name>JAX-RS REST Servlet</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>de.tsystems.lbus.apprestserver</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerResponseFilters</param-name>
<param-value>de.apprestserver.filter.RESTServiceResponseFilter</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>JAX-RS REST Servlet</servlet-name>
<url-pattern>/TNR/*</url-pattern>
</servlet-mapping>
I have no more ideas now and want to ask you, if you maybe have a solution for me. Thanks in advance!
You're using Jersey 1 (can tell by the com.sun.jersey in your web.xml). The filter implementation you are using (or showing us) is Jersey 2. There's subtle difference, but it's a major difference. The latter will not work with Jersey 1.
And the fact that it even compiles (if that's the case) means that you need to get rid of some dependencies. The class you have is a JAX-RS 2 class (interface). Any JAX-RS/Jersey 2 dependency, you might have, get rid of them. They don't play well (maybe not cause of issue, but get rid of them to drop any confusion)
Jersey 1 == com.sun.jersey (keep)
Jersey 2 == org.glassfish.jersey (get rid of)
JAX-RS 2 api == javax.ws.rs-api (get rid of)
See here for Jersey 1 implementation and configuration
Related
I've just started learning how to code REST web services, and I've been stuck with this for several days now. I'm coding an example application with header-based filtering, using Jersey 2 and deployed on Tomee-plus 1.7.2. No matter what I try, the ContainerRequestFilter's filter method is never called.
// TestRequestFilter.java
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.PreMatching;
import javax.ws.rs.ext.Provider;
#Provider
#PreMatching
public class TestRequestFilter implements ContainerRequestFilter
{
private final static Logger log = Logger.getLogger(DemoRESTRequestFilter.class.getName());
#Override
public void filter(ContainerRequestContext requestCtx) throws IOException
{
System.out.println("FILTER-REQUEST");
}
}
My web.xml file is empty save for the required headers. The behavior right now is: the filter is recognised as #Provider and instantiated as normal, the test web service I have (just a GET returning an empty Response) can be called normally, but the filter method is never called.
Things I've tried and their effects:
Declare the filter in a class extending Application: Error on deployment.
Register the filter in a class extending ResourceConfig: Filter is instantiated twice, but filter method is still not called.
Use the classes from the com.sun.jersey.spi.container package: No effect.
Add an authentication annotation (#RolesAllowed, #PermitAll,...) to the WS method: No effect.
Add disabled=true to cfx-rs.properties in server configuration: Deployed service cannot be found at usual URL.
Add this to web.xml: No effect.
<servlet>
<servlet-name>CongressAppWS</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>org.glassfish.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.s4w.congressapp.auth.DemoRESTRequestFilter;com.s4w.congressapp.auth.DemoRESTResponseFilter</param-value>
</init-param>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.s4w.congressapp.auth;com.s4w.congressapp.resources</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
Using com.sun.jersey.spi.container prefix instead of org.glassfish.jersey.spi.container in previous code: No effect.
I'm honestly running out of options here. Every time I try something new, either there is no effect or everything stops working. Any help?
I found an alternative to ContainerRequestFilter that actually works! It's javax.servlet.Filter. Extending from this class, all I had to do was annotate it with the following code and the filtering mechanism works like a charm.
#WebFilter(filterName = "AuthenticationFilter", urlPatterns = { "/*" })
The reason for this is that ContainerRequestFilter is part JAX-RS 2.0, but TomEE 1.7.X comes with JAX-RS 1.1. You'll either have to upgrade TomEE to 7.0.0+ or use a different Server (e.g. Glassfish).
I am trying to add a Jersey2 ContainerRequestFilter to a webapp via web.xml. Currently I always use the notation with a servlet as described here:
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
...
</init-param>
</servlet>
...
<servlet-mapping>
<servlet-name>MyApplication</servlet-name>
<url-pattern>/myApp/*</url-pattern>
</servlet-mapping>
Now in a different case, I dont have a servlet but a wicket app running on tomcat, that I want to use my filter with. So I tried to use the alternative filter notation, described in the same article as above (example 4.10):
<filter>
<filter-name>MyApplication</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
...
</init-param>
</filter>
...
<filter-mapping>
<filter-name>MyApplication</filter-name>
<url-pattern>/myApp/*</url-pattern>
</filter-mapping>
I successfully used it with the Jersey1 filter notation but am unable to make it work with the Jersey2 filter (this one is more up to date, so I prefer it over the older one).
Unfortunately I found the documenation of this feature being rather poor. Can anyone help if this might work and if so, how to do it?
As mentioned by #peeskillet in the comments, I had to add the #Provider annotation on top of the Filter for it to be picked up by the Jersey. Adding this answer for anyone who is trying to find the solution. The same can be done for the ContainerResponseFilter also.
import java.io.IOException;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;
#Provider
public class LoggingFilter implements ContainerRequestFilter,ContainerResponseFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
//DO Stuff
}
#Override
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
throws IOException {
//DO Stuff
}
}
I recently set up RESTEasy on Tomcat 7. We do not use Maven. I mention this early on, because all of the answers I've found to my question to date suggest adding a reference to the pom.xml. Adding references to my project, so far, hasn't solved my problem (but, maybe I'm missing something).
At this point, I'm just trying to recreate the out-of-the-box examples I've seen online where I should be able to correctly annotate a POJO, and return it as JSON using RESTEasy, assuming I have my build configured correctly. My build references:
- jackson.core.asl
- jackson.jaxrs
- jackson.mapper.asl
and all the required RESTEasy jars. In fact, I can successfully obtain the JSON I want from a GET if I return a Response (as in the example I found here: RestEasy: Could not find MessageBodyWriter for response object of type: java.util.ArrayList of media type: application/json). Of course, that's not using Jackson through RESTEasy, at all.
Whenever I try to return an object, however, of any kind, I get:
Could not find MessageBodyWriter for response object of type: ... application/json
I'm not sure what I'm doing wrong, as all the answers I can find suggest that what I need to do is make sure that jackson is correctly referenced---which, from all I can tell, it is. Below is the relevant portion of my web.xml.
<listener>
<listener-class>
org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
</listener-class>
</listener>
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>rest_services.RootRestService</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/services/*</url-pattern>
</servlet-mapping>
<context-param>
<param-name>resteasy.scan</param-name>
<param-value>true</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/services</param-value>
</context-param>
Here is my Service method:
#GET
#Path("/getTables")
#Produces(MediaType.APPLICATION_JSON)
public List<MyTable> getTables() {
...
}
Here is the class it's returning:
#XmlRootElement
public final class MyTable {
...
}
EDIT :
Here are the classes I added to my classpath (and a few others)
:
Any help is greatly appreciated.
I was never able to get Jackson working, but I switched to Jettison, and all works fine. This is good enough for me, for now.
I am building servlets which implement a RESTful API. I understand the Jersey is a framework for deciphering and using given URL's. How do I use it in conjunction with the HttpServlet class.
I don't understand how the two work with each other. I guess this is a very broadstrokes question but I have done a fair share of reading but am still stuck on this seemingly trivial concept. I have attempted to deploy apps with classes that extend the HttpServletclass AND use Jersey annotations.
#Path("/api")
public class API extends HttpServlet{
#GET
#Path("/{name}")
#Produces("text/hmtl")
public String doGetSayHello(#PathParam("name") String name){
return "Hello" + name;
}
#GET
#Path("/articles")
#Produces("text/json")
public String doGetArticles(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
JSONObject obj = new JSONObject();
obj.put("interns", interns);
obj.put("company", "Stack Overflow");
return obj.toString();
}
}
Any help or informative materials would be greatly appreciated!
Actually you are confused because you don't understand how jersey works. Jersey framework basically uses com.sun.jersey.spi.container.servlet.ServletContainer servlet to intercept all the incoming requests. As we configure in our projects web.xml, that all the incoming rest request should be handled by that servlet. There is an init-param that is configured with the jersey servlet to find your REST service classes. REST service classes are not Servlet and they need NOT to extend the HttpServlet as you did in your code. These REST service classes are simple POJOs annotated to tell the jersey framework about different properties such as path, consumes, produces etc. When you return from your service method, jersey takes care of marshalling those objects in the defined 'PRODUCES' responseType and write it on the client stream. Here is a sample of jersey config in 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>
com.rest.services;
</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>
Jersey uses a servlet to route URLs to the appropriate service. Your service itself does not need to extend a servlet.
At a high level, Jersey's ServletContainer class accepts the requests, and then based on your Jersey configuration, your web service will be invoked. You configure what url patterns are processed by Jersey. Check out section 5.3 http://www.vogella.com/articles/REST/.
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("/*");