Tornado服务器:启用CORS请求

时间:2016-02-07 14:33:27

标签: cors tornado

我有一个简单的龙卷风服务器,它有类:

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=301880
  -->
<configuration>
  <configSections>

    <section name="entityFramework" type="System.Data.Entity.Internal.ConfigFile.EntityFrameworkSection, EntityFramework, Version=6.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
  <!-- For more information on Entity Framework configuration, visit http://go.microsoft.com/fwlink/?LinkID=237468 --></configSections>
  <connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=(LocalDb)\v11.0;AttachDbFilename=|DataDirectory|\aspnet-OWIN2-20160208103141.mdf;Initial Catalog=aspnet-OWIN2-20160208103141;Integrated Security=True" providerName="System.Data.SqlClient" />
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <system.web>
    <authentication mode="None" />
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.webServer>
    <modules>
      <remove name="FormsAuthenticationModule" />
    </modules>
  </system.webServer>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.6.5135.21930" newVersion="1.6.5135.21930" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Antlr3.Runtime" publicKeyToken="eb42632606e9261f" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.2" newVersion="3.5.0.2" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.AspNet.Identity.Core" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-2.0.0.0" newVersion="2.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-5.2.3.0" newVersion="5.2.3.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <entityFramework>
    <defaultConnectionFactory type="System.Data.Entity.Infrastructure.LocalDbConnectionFactory, EntityFramework">
      <parameters>
        <parameter value="v11.0" />
      </parameters>
    </defaultConnectionFactory>
    <providers>
      <provider invariantName="System.Data.SqlClient" type="System.Data.Entity.SqlServer.SqlProviderServices, EntityFramework.SqlServer" />
    </providers>
  </entityFramework>
</configuration>

当发出常规(无CORS)请求时,服务器按预期应答,包括Access-Control-Allow-Origin标头。但是当我发出来自不同域的帖子请求时(使用class BaseHandler(tornado.web.RequestHandler): def set_default_headers(self): print "setting headers!!!" self.set_header("Access-Control-Allow-Origin", "*") ),响应为404并显示错误:“XMLHttpRequest无法加载http://dev-machine:8090/handshake。否'Access-Control-Allow-Origin'标头出现在请求的资源上。因此不允许原点“http://localhost:8090”访问。响应的HTTP状态代码为404.“

你能告诉我是否错过了什么吗? (另一个标题/其他配置/其他任何东西)

4 个答案:

答案 0 :(得分:31)

您的代码缺少预检,OPTIONS请求。

https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS

  

跨域资源共享标准通过添加新HTTP来工作   允许服务器描述原始集合的标头   允许使用Web浏览器读取该信息。另外,   对于可能对用户数据造成副作用的HTTP请求方法(in   特别是,对于GET以外的HTTP方法,或者对于POST使用   某些MIME类型),规范要求浏览器   “预检”请求,从服务器请求支持的方法   使用HTTP OPTIONS请求方法,然后,在“批准”时   服务器,使用实际的HTTP请求发送实际请求   方法。服务器还可以通知客户端是否“凭据”   (包括Cookie和HTTP身份验证数据)应与之一起发送   请求。

要实现预检处理程序,只需添加具有相同标题且没有正文的选项处理程序。

class BaseHandler(tornado.web.RequestHandler):

    def set_default_headers(self):
        print "setting headers!!!"
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header("Access-Control-Allow-Headers", "x-requested-with")
        self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS')

    def post(self):
        self.write('some post')

    def get(self):
        self.write('some get')

    def options(self):
        # no body
        self.set_status(204)
        self.finish()

修改

我已将x-requested-with标头添加到允许列表中。这里是简单的jquery示例:

 $.ajax({
   url: "http://some_tornado/api",
   type: "POST",
   crossDomain: true,
   data: 'some_data',
   success: function (response) {
     alert(response);
   },
   error: function (xhr, status) {
     alert("error");
   }
 });

关于cors的一些非常好的文章 - http://dev.housetrip.com/2014/04/17/unleash-your-ajax-requests-with-cors/

答案 1 :(得分:7)

kwarunek的回答让我解决了PUT和DELETE请求的问题。唯一的问题是,该解决方案对于使用GET和POST的示例而言过于合适。在这种情况下,行

self.set_header("Access-Control-Allow-Origin", "*")

实际上已经足够了(如果浏览器之前没有阻止CORS)。它与PUT和DELETE请求最相关。在网络级别上发生的事情可能比在GET / POST情况下稍微复杂一些。

“如果请求是”非简单“请求,浏览器首先发送无数据”预检“OPTIONS请求,以验证服务器是否接受请求。使用HTTP时请求不简单除GET或POST之外的动词(例如PUT,DELETE)。“ cf. non-simple requests

class BaseHandler(tornado.web.RequestHandler):

    def set_default_headers(self):
        print("setting headers!!!")
        self.set_header("Access-Control-Allow-Origin", "*")
        self.set_header("Access-Control-Allow-Headers", "x-requested-with")
        self.set_header('Access-Control-Allow-Methods', ' PUT, DELETE, OPTIONS')

    def options(self):
        # no body
        self.set_status(204)
        self.finish()

现在,所有继承自BaseHandler的处理程序都具备完全支持CORS的能力:

class MyHandler(BaseHandler):

    def put(self):
        self.write('some post')

    def delete(self):
        self.write('some get')

答案 2 :(得分:2)

这对我有用。

def set_default_headers(self):
    self.set_header("Content-Type", "application/json")
    self.set_header("Access-Control-Allow-Origin", "*")
    self.set_header("Access-Control-Allow-Headers", "content-type")
    self.set_header('Access-Control-Allow-Methods', 'POST, GET, OPTIONS, PATCH, PUT')

答案 3 :(得分:1)

即使使用之前的答案,我仍然遇到以下CORS错误:

  

阻止跨源请求:同源策略禁止读取   远程资源在   http://127.0.0.1:9999/home?message=Input%20to%20API ..(原因:失踪   CORS标头中的令牌“access-control-allow-origin”   来自CORS预检频道的“Access-Control-Allow-Headers”。

解决方案 也允许标题:

class BaseHandler(tornado.web.RequestHandler):

    def set_default_headers(self):
        print("setting headers!!!")
        self.set_header("access-control-allow-origin", "*")
        self.set_header("Access-Control-Allow-Headers", "x-requested-with")
        self.set_header('Access-Control-Allow-Methods', 'GET, PUT, DELETE, OPTIONS')
        # HEADERS!
        self.set_header("Access-Control-Allow-Headers", "access-control-allow-origin,authorization,content-type") 

    def options(self):
        # no body
        self.set_status(204)
        self.finish()