How to use CORS in Restlet 2.3.1?

2020-07-23 05:16发布

问题:

I'm trying to set the 'Access-Control-Allow-Origin: *' header for the response. However, the header is not present. What am I doing wrong?

public class JsonApplication extends Application
{
    private static final String SERVER_URL = "http://localhost:8111";

    public static void main(String[] args) throws Exception 
    {
        Server testServer = new Server(Protocol.HTTP, 8111);        
        JsonApplication jsonApplication = new JsonApplication();

        CorsService corsService = new CorsService();         
        corsService.setAllowedOrigins( new HashSet(Arrays.asList("*")));
        corsService.setAllowedCredentials(true);

        jsonApplication.getServices().add( corsService );      

        testServer.setNext( jsonApplication );
        testServer.start();
    }

    @Override
    public Restlet createInboundRoot()
    {
        Router router = new Router( getContext() );
        router.attach( SERVER_URL + "/", RootResource.class );        
        return router;
    }
}

I checked the source code for org.restlet.engine.application.CorsResponseHelper and it contains the following code:

public void addCorsResponseHeaders(Request request, Response response) {

String origin = request.getHeaders().getFirstValue("Origin", true);

if (origin == null) {
    // Not a CORS request
    return;
}
...
}

So, it appears that the current CORS implementation does not support requests made from a local html-file, since in that case origin == null.

I added some logging to my servlet application:

JsonApplication::createInboundRoot()
16:26 MyCorsService()
16:26 myCorsService = wwwdbase.rest.cors.MyCorsService@6e1b241d
16:26 MyCorsService::setAllowedOrigins(), [*]
16:26 services: [org.restlet.service.TunnelService@4c88fe62, 
org.restlet.service.StatusService@68349a5b,
org.restlet.service.DecoderService@77cfd8d3,
org.restlet.service.EncoderService@40c331fb,
org.restlet.service.RangeService@4bb3bc6f,
org.restlet.service.ConnectorService@7990100,
org.restlet.service.ConnegService@e194860,
org.restlet.service.ConverterService@578cfcb1,
org.restlet.service.MetadataService@18a62eb,
org.restlet.service.TaskService@4ed4f2db,
wwwdbase.rest.cors.MyCorsService@6e1b241d]

We can see that MyCorsService is available. However, it is never called by the servlet framework. On the other hand, if I run the service (the Java SE version) from IDE, MyCorsService is called. Why do these cases behave differently?

Partial solution: I managed to add the allow origin header by changing the code in org.restlet.engine.header.HeaderUtils

if (response.getAccessControlAllowOrigin() != null) 
{          
    addHeader(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
            response.getAccessControlAllowOrigin(), headers);
}

to

if (response.getAccessControlAllowOrigin() != null) 
{          
    addHeader(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
            response.getAccessControlAllowOrigin(), headers);
}
else
{
    // --- Add in any case!
    //           
    response.setAccessControlAllowOrigin( "*" );
    addHeader(HeaderConstants.HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
                    response.getAccessControlAllowOrigin(), headers);            
}

However, the answer to the question why the servlet framework in Tomcat did not call the cors service is still unknown...

回答1:

In fact, you have correctly configured the CORS service of Restlet ;-)

In the context of CORS, there are two kinds of requests:

  • Simple requests. To be short, when you use methods GET, HEAD and some cases with POST. In this case, CORS headers aren't required when doing cross domain requests.
  • Preflighted requests The other cases where CORS headers are required to do cross domain requests.

So if you simply do a method GET, it's normal that you saw nothing. Try with a method POST with a JSON content and you will see the CORS headers.

For more details, you can have a look at this link:

  • Understanding and using CORS - https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/

Edited

I made a more complete test for your issue. I have two Restlet server launched on different ports (8182 for the resource to be accessed using AJAX, 8183 for the JS application that accesses the resource).

The first one is configured with your code:

CorsService corsService = new CorsService();         
corsService.setAllowedOrigins(new HashSet(Arrays.asList("*")));
corsService.setAllowedCredentials(true);

RestletApplication application = new RestletApplication();
application.getServices().add(corsService);
component.getDefaultHost().attachDefault(application);

The server resource in it is simple:

public class MyServerResource extends ServerResource {
    @Get
    public TestBean ping() {
        TestBean bean = new TestBean();
        bean.setMessage("pong");
        return bean;
    }
}

The second application only uses a Restlet directory to serve static content:

@Override
public Restlet createInboundRoot() {
    Router router = new Router(getContext());
    router.attach("/static", new Directory(
            getContext(), "clap:///test/static"));
    return router;
}

In addition to JQuery library, I added the following HTML file:

<html>
  <head>
    <script type="text/javascript" src="/static/jquery-1.11.2.min.js"></script>
    <script type="text/javascript">
    $(document).ready(function() {
      $('#id1').click(function() {
        $.ajax({
           url : 'http://localhost:8182/ping',
           type : 'GET',
           success : function(result, status){
               console.log('ok = '+result);
           },
           error : function(result, status, error){
               console.log('error');
           },
           complete : function(result, status){
                console.log('complete');
           }
        });
      });
    });
    </script>
  </head>
  <body>
    <div id="id1">Click</div>
  </body>
</html>

Here is what I have when I clicked on the button "Click" at the header level:

// Request
Accept  */*
Accept-Encoding gzip, deflate
Accept-Language fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3
Host    localhost:8182
Origin  http://localhost:8183
Referer http://localhost:8183/static/test.html
User-Agent  Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:36.0) Gecko/20100101 Firefox/36.0

// Response
Accept-Ranges   bytes
Access-Control-Allow-Cred...    true
Access-Control-Allow-Orig...    http://localhost:8183
Content-Type    application/json
Date    Fri, 13 Mar 2015 09:16:48 GMT, Fri, 13 Mar 2015 09:16:48 GMT
Server  Restlet-Framework/2.3.1
Transfer-Encoding   chunked
Vary    Accept-Charset, Accept-Encoding, Accept-Language, Accept

As you can see, the CORS are present ;-)

Hope it helps you, Thierry



标签: cors restlet