如何在Restlet 2.3.1中使用CORS?

3
我正在尝试为响应设置“Access-Control-Allow-Origin: *”头。然而,这个头并不存在。我做错了什么?
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;
    }
}

我查看了org.restlet.engine.application.CorsResponseHelper的源代码,其中包含以下代码:
public void addCorsResponseHeaders(Request request, Response response) {

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

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

因为在这种情况下 origin == null,所以目前的CORS实现似乎不支持从本地html文件发出的请求。

我在我的servlet应用程序中添加了一些日志记录:

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]

我们可以看到MyCorsService可用,但它从未被Servlet框架调用。另一方面,如果我从IDE运行服务(Java SE版本),则会调用MyCorsService。为什么这些情况会有不同的行为呢?
部分解决方案:我通过更改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);            
}

然而,为什么Tomcat中的servlet框架没有调用cors服务的答案仍然未知。
1个回答

2
事实上,您已经正确配置了Restlet的CORS服务 ;-)
在CORS的上下文中,有两种请求类型:
- 简单请求。简单来说,当您使用`GET`、`HEAD`方法和某些情况下使用`POST`方法时。在这种情况下,进行跨域请求时不需要CORS头。
- 预检请求。其他情况下需要CORS头来进行跨域请求。
因此,如果您只是使用`GET`方法,没有看到任何内容是正常的。尝试使用带有JSON内容的`POST`方法,您将看到CORS头。
更多细节,请查看以下链接:
- 理解和使用CORS - https://templth.wordpress.com/2014/11/12/understanding-and-using-cors/ 我为您的问题做了一个更完整的测试。我启动了两个不同端口的Restlet服务器(8182用于通过AJAX访问资源,8183用于访问资源的JS应用程序)。
第一个服务器使用您的代码进行配置:
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);

它中的服务器资源很简单:
public class MyServerResource extends ServerResource {
    @Get
    public TestBean ping() {
        TestBean bean = new TestBean();
        bean.setMessage("pong");
        return bean;
    }
}

第二个应用程序仅使用Restlet目录来提供静态内容:
@Override
public Restlet createInboundRoot() {
    Router router = new Router(getContext());
    router.attach("/static", new Directory(
            getContext(), "clap:///test/static"));
    return router;
}

除了 JQuery 库,我还添加了以下 HTML 文件:
<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>

这是我在点击标题级别上的“点击”按钮时看到的内容:
// 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

如您所见,CORS已经存在;-)
希望对您有所帮助, Thierry

谢谢您的回答。我正在开发一个返回JSON格式数据的RESTful服务。客户端可以开发自己基于浏览器的UI,并使用AJAX调用与服务器交互。默认情况下禁止跨域AJAX调用,因此即使使用GET,也需要CORS(或JSONP)。然而,我通过直接在浏览器地址栏中编写请求URL来测试服务器调用。显然,那不是一个正确的AJAX调用,所以如果缺少头文件就不足为奇了。我默认认为如果CORS配置正确,Restlet框架总是会包括头文件... - matt
我更新了我的回答,加入了更完整的测试。看起来,在一个 AJAX 请求中,CORS 标头是存在的 😉 - Thierry Templier
我现在已经在本地Tomcat服务器下安装了我的应用程序。如果我通过jQuery Ajax从本地html文件调用服务,我会得到响应:“跨域请求被阻止:同源策略不允许读取远程资源http://localhost:8080/rest/authentication。这可以通过将资源移动到相同的域或启用CORS来解决。”所以,看起来CORS不起作用? - matt
1
在基于HTML文件的调用中,存在一个请求头'Origin null'。也许这就是问题的根源。如果是这样,该如何解决? - matt
1
请编辑此答案,它是误导性的。 "简单请求" 也需要 CORS 标头,你链接的博客文章证实了这一点。 - letmaik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接