AngularJS为跨源资源执行OPTIONS HTTP请求

时间:2012-08-24 15:02:50

标签: angularjs cors cross-domain preflight

我正在尝试将AngularJS设置为与跨源资源进行通信,其中传递我的模板文件的资产主机位于不同的域上,因此角度执行的XHR请求必须是跨域的。我已经为我的服务器添加了适当的CORS标头,以便使其工作,但它似乎不起作用。问题是,当我在浏览器(chrome)中检查HTTP请求时,发送到资产文件的请求是OPTIONS请求(它应该是GET请求)。

我不确定这是否是AngularJS中的错误,或者我是否需要配置某些内容。根据我的理解,XHR包装器无法发出OPTIONS HTTP请求,因此看起来浏览器正在尝试确定在执行GET请求之前是否“允许”首先下载资产。如果是这种情况,那么我是否还需要为资产主机设置CORS标头(Access-Control-Allow-Origin:http://asset.host..。)?

14 个答案:

答案 0 :(得分:221)

OPTIONS请求绝不是AngularJS错误,这就是跨源资源共享标准要求浏览器行为的方式。请参阅此文档:https://developer.mozilla.org/en-US/docs/HTTP_access_control,其中“概述”部分中的内容为:

  

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

很难提供适用于所有WWW服务器的通用解决方案,因为设置会因服务器本身和您打算支持的HTTP谓词而异。我建议您阅读这篇优秀文章(http://www.html5rocks.com/en/tutorials/cors/),其中详细介绍了服务器需要发送的确切标题。

答案 1 :(得分:68)

对于Angular 1.2.0rc1 +,您需要添加一个resourceUrlWhitelist。

1.2:发布版本他们添加了一个escapeForRegexp函数,因此您不再需要转义字符串。您可以直接添加网址

'http://sub*.assets.example.com/**' 

确保为子文件夹添加**。这是一个适用于1.2的工作jsbin: http://jsbin.com/olavok/145/edit


1.2.0rc:如果您仍然使用rc版本,Angular 1.2.0rc1的解决方案如下:

.config(['$sceDelegateProvider', function($sceDelegateProvider) {
     $sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]);
 }])

这是一个jsbin示例,它适用于1.2.0rc1: http://jsbin.com/olavok/144/edit


Pre 1.2:对于旧版本(参考http://better-inter.net/enabling-cors-in-angular-js/),您需要在配置中添加以下两行:

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];

这是一个jsbin示例,它适用于1.2之前的版本: http://jsbin.com/olavok/11/edit

答案 2 :(得分:61)

注意:不确定它是否适用于最新版本的Angular。

<强> ORIGINAL:

也可以覆盖OPTIONS请求(仅在Chrome中测试):

app.config(['$httpProvider', function ($httpProvider) {
  //Reset headers to avoid OPTIONS request (aka preflight)
  $httpProvider.defaults.headers.common = {};
  $httpProvider.defaults.headers.post = {};
  $httpProvider.defaults.headers.put = {};
  $httpProvider.defaults.headers.patch = {};
}]);

答案 3 :(得分:34)

您的服务必须回复OPTIONS请求,其中包含以下标题:

Access-Control-Allow-Origin: [the same origin from the request]
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: [the same ACCESS-CONTROL-REQUEST-HEADERS from request]

这是一个很好的文档:http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server

答案 4 :(得分:20)

同一份文件说

与简单请求(如上所述)不同,“预检”请求首先将HTTP OPTIONS请求标头发送到另一个域上的资源,以确定实际请求是否可安全发送。跨站点请求是这样预检的,因为它们可能对用户数据有影响。特别是,如果出现以下情况,请求会被预检:

  

它使用GET或POST以外的方法。此外,如果使用POST来发送具有除application / x-www-form-urlencoded,multipart / form-data或text / plain之外的Content-Type的请求数据,例如,如果POST请求使用application / xml或text / xml向服务器发送XML有效负载,则该请求将被预检。

     

它在请求中设置自定义标头(例如,请求使用标头,例如X-PINGOTHER)

如果原始请求是“没有自定义标头的Get”,则浏览器不应该发出它现在执行的“选项”请求。问题是它生成一个标题X-Requested-With强制Options请求。有关如何删除此标题的信息,请参阅https://github.com/angular/angular.js/pull/1454

答案 5 :(得分:10)

这解决了我的问题:

$http.defaults.headers.post["Content-Type"] = "text/plain";

答案 6 :(得分:10)

如果您使用的是nodeJS服务器,则可以使用此库,它对我来说运行良好https://github.com/expressjs/cors

var express = require('express')
  , cors = require('cors')
  , app = express();

app.use(cors());

然后你可以做npm update

答案 7 :(得分:4)

以下是我在ASP.NET上解决此问题的方法

  • 首先,您应该添加nuget包 Microsoft.AspNet.WebApi.Cors

  • 然后修改文件App_Start \ WebApiConfig.cs

    public static class WebApiConfig    
    {
       public static void Register(HttpConfiguration config)
       {
          config.EnableCors();
    
          ...
       }    
    }
    
  • 在控制器类上添加此属性

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {  
        [AcceptVerbs("POST")]
        public IHttpActionResult Post([FromBody]YourDataType data)
        {
             ...
             return Ok(result);
        }
    }
    
  • 我能够通过这种方式将json发送到动作

    $http({
            method: 'POST',
            data: JSON.stringify(data),
            url: 'actionurl',
            headers: {
                'Content-Type': 'application/json; charset=UTF-8'
            }
        }).then(...)
    

参考: Enabling Cross-Origin Requests in ASP.NET Web API 2

答案 8 :(得分:2)

不知何故,我通过改变

修复了它
DEALER

<add name="Access-Control-Allow-Headers" 
     value="Origin, X-Requested-With, Content-Type, Accept, Authorization" 
     />

答案 9 :(得分:0)

在pkozlowski的评论中有完美的描述。 我使用AngularJS 1.2.6和ASP.NET Web Api工作解决方案,但当我将AngularJS升级到1.3.3时,请求失败。

  • Web Api服务器的解决方案是在配置方法的开头添加OPTIONS请求的处理(更多信息in this blog post):

    app.Use(async (context, next) =>
    {
        IOwinRequest req = context.Request;
        IOwinResponse res = context.Response;
        if (req.Path.StartsWithSegments(new PathString("/Token")))
        {
            var origin = req.Headers.Get("Origin");
            if (!string.IsNullOrEmpty(origin))
            {
                res.Headers.Set("Access-Control-Allow-Origin", origin);
            }
            if (req.Method == "OPTIONS")
            {
                res.StatusCode = 200;
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Methods", "GET", "POST");
                res.Headers.AppendCommaSeparatedValues("Access-Control-Allow-Headers", "authorization", "content-type");
                return;
            }
        }
        await next();
    });
    

答案 10 :(得分:0)

如果您使用Jersey for REST API,则可以执行以下操作

您无需更改Web服务实施。

我将为Jersey 2.x解释

1)首先添加一个ResponseFilter,如下所示

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;

public class CorsResponseFilter implements ContainerResponseFilter {

@Override
public void filter(ContainerRequestContext requestContext,   ContainerResponseContext responseContext)
    throws IOException {
        responseContext.getHeaders().add("Access-Control-Allow-Origin","*");
        responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, DELETE, PUT");

  }
}

2)然后在web.xml中,在jersey servlet声明中添加以下

    <init-param>
        <param-name>jersey.config.server.provider.classnames</param-name>
        <param-value>YOUR PACKAGE.CorsResponseFilter</param-value>
    </init-param>

答案 11 :(得分:0)

我放弃了尝试解决这个问题。

我的IIS web.config中包含相关的“Access-Control-Allow-Methods”,我尝试将配置设置添加到我的Angular代码中,但是在烧了几个小时后试图让Chrome调用跨域JSON Web服务我悲惨地放弃了。

最后,我添加了一个愚蠢的ASP.Net处理程序网页, 来调用我的JSON Web服务,并返回结果。它在2分钟内启动并运行。

这是我使用的代码:

public class LoadJSONData : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        context.Response.ContentType = "text/plain";

        string URL = "......";

        using (var client = new HttpClient())
        {
            // New code:
            client.BaseAddress = new Uri(URL);
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.Add("Authorization", "Basic AUTHORIZATION_STRING");

            HttpResponseMessage response = client.GetAsync(URL).Result;
            if (response.IsSuccessStatusCode)
            {
                var content = response.Content.ReadAsStringAsync().Result;
                context.Response.Write("Success: " + content);
            }
            else
            {
                context.Response.Write(response.StatusCode + " : Message - " + response.ReasonPhrase);
            }
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

在我的Angular控制器中......

$http.get("/Handlers/LoadJSONData.ashx")
   .success(function (data) {
      ....
   });

我确信有一种更简单/更通用的方式,但生命太短......

这对我有用,我现在可以继续正常工作!!

答案 12 :(得分:0)

对于使用API​​的IIS MVC 5 / Angular CLI(是的,我很清楚您的问题是Angular JS)项目,我执行了以下操作:

web.config <system.webServer>节点下

    <staticContent>
      <remove fileExtension=".woff2" />
      <mimeMap fileExtension=".woff2" mimeType="font/woff2" />
    </staticContent>
    <httpProtocol>
      <customHeaders>
        <clear />
        <add name="Access-Control-Allow-Origin" value="*" />
        <add name="Access-Control-Allow-Headers" value="Content-Type, atv2" />
        <add name="Access-Control-Allow-Methods" value="GET, POST, PUT, DELETE, OPTIONS"/>
      </customHeaders>
    </httpProtocol>

global.asax.cs

protected void Application_BeginRequest() {
  if (Request.Headers.AllKeys.Contains("Origin", StringComparer.OrdinalIgnoreCase) && Request.HttpMethod == "OPTIONS") {
    Response.Flush();
    Response.End();
  }
}

这应该可以解决MVC和WebAPI的问题,而不必执行其他所有操作。然后,我在Angular CLI项目中创建了一个HttpInterceptor,它自动添加了相关的头信息。希望这可以帮助处于类似情况的人。

答案 13 :(得分:0)

聚会晚一点,

如果您使用Angular 7(或5/6/7)和PHP作为API并仍然出现此错误,请尝试将以下标头选项添加到端点(PHP API)。

 header("Access-Control-Allow-Origin: *");
 header("Access-Control-Allow-Methods: PUT, GET, POST, PUT, OPTIONS, DELETE, PATCH");
 header("Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Authorization");

注意:只需Access-Control-Allow-Methods。但是,我将另外两个Access-Control-Allow-OriginAccess-Control-Allow-Headers粘贴到这里,仅仅是因为您需要正确设置所有这些内容,才能使Angular App与您的API正确对话。

希望这对某人有帮助。

干杯。