22

I am working on a webapplication comprises UI-Angular , Server-Java , RestEasy 3.0.9.Final for rest api calls

When i tried to access the rest service from another domain am getting below error

CANNOT LOAD Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:8080' is therefore not allowed access.

I configured my server side to respond with the cross domain calls and this is working with the GET Call but POST Call is creating ERROR

web.xml

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>com.test.sample.app.CorsFeature</param-value>
</context-param>

<listener>
    <listener-class>
        org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>


<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

<servlet>
    <servlet-name>resteasy-servlet</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>com.test.sample.app.Application</param-value>
    </init-param>
</servlet>

<servlet-mapping>
    <servlet-name>resteasy-servlet</servlet-name>
    <url-pattern>/rest/*</url-pattern>
</servlet-mapping>

<context-param>
    <param-name>resteasy.servlet.mapping.prefix</param-name>
    <param-value>/rest</param-value>
</context-param>
<context-param>
    <param-name>resteasy.scan</param-name>
    <param-value>true</param-value>
</context-param>

Service class

@GET
@Path("/getnameAtt")
@Produces(MediaType.APPLICATION_JSON)
public Response getHostnameAttributes() {
return Response
.status(200)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers",
        "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods",
        "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600")
.entity(new TestImpl().getHostNameAttributes())
.build();
}

@POST
@Path("/getSeq")
@Produces(MediaType.APPLICATION_JSON)
public Response getCurrentSequence(String request) {
return Response
.status(200)
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Headers",
        "origin, content-type, accept, authorization")
.header("Access-Control-Allow-Credentials", "true")
.header("Access-Control-Allow-Methods",
        "GET, POST, PUT, DELETE, OPTIONS, HEAD")
.header("Access-Control-Max-Age", "1209600")
.entity(new TestImpl().getCurrentSeq(request))
.build();
}

Since i am new to resteasy not able to figure out why this was not working. Any help would be greatly appreciated . Waiting for your response.

Thanks

sideshowbarker
  • 81,827
  • 26
  • 193
  • 197
PremKumarR
  • 461
  • 1
  • 6
  • 19

3 Answers3

11

Your resource methods won't get hit, so their headers will never get set. The reason is that there is what's called a preflight request before the actual request, which is an OPTIONS request. So the error comes from the fact that the preflight request doesn't produce the necessary headers.

For RESTeasy, you should use CorsFilter. You can see here for some example how to configure it. This filter will handle the preflight request. So you can remove all those headers you have in your resource methods.

See Also:

Community
  • 1
  • 1
Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
  • Thanks for the response . Have used that CorsFilter and annotated with @Provider . but that class is not even called . Do i need to add anything in web.xml – PremKumarR Oct 16 '15 at 08:28
  • That example I linked to was for if you are scanning for resources. If you explicitly registering all your resource and providers, then you can just register the `CorsFilter` in your `Application` class. Or register the `Feature`. Either way works. The `Feature` is not needed though. You can just register the `CorsFilter` in your override of `getSingletons()` in your `Application` class – Paul Samsotha Oct 16 '15 at 08:31
  • should the preflight request be explicit or is it handled by the browser internally? Do I have to send an OPTION preflight request first before sending the actual request? I am stuck at similar error. – Sudip Bhandari Jul 23 '16 at 10:04
  • @SudipBhandari No the whole interaction is handled transparently by the browser for any request that requires cross origin check. You only need to have the server set up to handle the OPTIONS preflight (by sending the appropriate CORS response headers). The `CorsFilter` mentioned in this post does just that. – Paul Samsotha Jul 23 '16 at 10:10
  • @peeskillet So if I am making a POST to an API, that API should handle the preflight OPTIONS too? I am asking this because the response is OK when done through clients like postman. I have custom header in my post request and still it cant pass access control check. Also the error message in chrome console is "No Access-Control-Allow-Origin present" although it clearly is and otherwise how would something like 'postman' receive the response successfully. Help me out – Sudip Bhandari Jul 23 '16 at 10:52
  • @SudipBhandari Because Postman is not using XHR which requires a cross origin check. AJAX (browser) requests use XHR. The browser detects this, and send the OPTIONS request before making the POST request. Your server needs to handle this OPTIONS request appropriately by sending the required headers. When the browser gets the OPTIONS response back with the correct headers, it will then send the POST request. If the OPTIONS response does not send the correct response headers, then the browser will not make the POST request, instead gives you an error. – Paul Samsotha Jul 23 '16 at 10:58
  • @peeskillet Okay, So how do I handle OPTIONS in server separately? And since http is sessionless how is it guaranteed that the following request is from the same client. Is it confirmed from the custom header that we set for the preflight? – Sudip Bhandari Jul 23 '16 at 11:01
  • @SudipBhandari This particular question is about RESTEasy, as Java REST framework. With this framework, you can simply use a filter, specificly the `CorsFilter` (which intercepts all preflight requests). Whatever framework you are using, you will need to do something specific to that framework. – Paul Samsotha Jul 23 '16 at 11:11
  • http://stackoverflow.com/questions/41854438/how-to-use-api-key-along-with-cors-in-web-api I had it working correctly with CORS but with new security added I am getting the error. – Si8 Jan 25 '17 at 14:47
0

Seems your resource POSTmethod won't get hit as @peeskillet mention. Most probably your ~POST~ request won't work, because it may not be a simple request. The only simple requests are GET, HEAD or POST and request headers are simple(The only simple headers are Accept, Accept-Language, Content-Language, Content-Type= application/x-www-form-urlencoded, multipart/form-data, text/plain).

Since in you already add Access-Control-Allow-Origin headers to your Response, you can add new OPTIONS method to your resource class.

    @OPTIONS
@Path("{path : .*}")
public Response options() {
    return Response.ok("")
            .header("Access-Control-Allow-Origin", "*")
            .header("Access-Control-Allow-Headers", "origin, content-type, accept, authorization")
            .header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD")
            .header("Access-Control-Max-Age", "2000")
            .build();
}
Ranuka
  • 773
  • 7
  • 13
0

After facing a similar issue, below is what I did :

  • Created a class extending javax.ws.rs.core.Application and added a Cors Filter to it.

To the CORS filter, I added corsFilter.getAllowedOrigins().add("http://localhost:4200");.

Basically, you should add the URL which you want to allow Cross-Origin Resource Sharing. Ans you can also use "*" instead of any specific URL to allow any URL.

public class RestApplication
    extends Application
{
    private Set<Object> singletons = new HashSet<Object>();

    public MessageApplication()
    {
        singletons.add(new CalculatorService()); //CalculatorService is your specific service you want to add/use.
        CorsFilter corsFilter = new CorsFilter();
        // To allow all origins for CORS add following, otherwise add only specific urls.
        // corsFilter.getAllowedOrigins().add("*");
        System.out.println("To only allow restrcited urls ");
        corsFilter.getAllowedOrigins().add("http://localhost:4200");
        singletons = new LinkedHashSet<Object>();
        singletons.add(corsFilter);
    }

    @Override
    public Set<Object> getSingletons()
    {
        return singletons;
    }
}
  • And here is my web.xml:
<web-app id="WebApp_ID" version="2.4"
    xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee 
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <display-name>Restful Web Application</display-name>

    <!-- Auto scan rest service -->
    <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>/rest</param-value>
    </context-param>

    <listener>
        <listener-class>
            org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap
        </listener-class>
    </listener>

    <servlet>
        <servlet-name>resteasy-servlet</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>com.app.RestApplication</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>resteasy-servlet</servlet-name>
        <url-pattern>/rest/*</url-pattern>
    </servlet-mapping>

</web-app>

The most important code which I was missing when I was getting this issue was, I was not adding my class extending javax.ws.rs.Application i.e RestApplication to the init-param of <servlet-name>resteasy-servlet</servlet-name>

   <init-param>
        <param-name>javax.ws.rs.Application</param-name>
        <param-value>com.app.RestApplication</param-value>
    </init-param>

And therefore my Filter was not able to execute and thus the application was not allowing CORS from the URL specified.

Deep
  • 929
  • 2
  • 16
  • 32