AngularJS对跨域资源执行OPTIONS HTTP请求


264

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

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

Answers:


226

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

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

提供一个适用于所有WWW服务器的通用解决方案非常困难,因为设置会因服务器本身和您打算支持的HTTP谓词而异。我鼓励您阅读这篇出色的文章(http://www.html5rocks.com/en/tutorials/cors/),其中包含有关服务器需要发送的确切标头的更多详细信息。


23
@matsko您能详细说明解决此问题的方法吗?我面临着一个相同的问题,即AngularJS $resource POST请求正在向后端ExpressJS服务器(在同一主机上,但在不同端口上)生成OPTIONS请求。
dbau 2012年

6
对于所有拒绝投票的人-几乎不可能为所有Web服务器提供准确的配置设置-在这种情况下,答案是10页。相反,我链接到提供更多详细信息的文章。
pkozlowski.opensource

5
没错,您不能说出答案-您需要在OPTIONS响应中添加标头,以覆盖浏览器请求的所有标头,在我的情况下,使用chrome,标头为'accept'和'x -要求-”。在chrome中,我通过查看网络请求并查看了chrome的要求来确定需要添加的内容。我使用nodejs / expressjs作为后盾,因此我创建了一个服务,该服务返回了对OPTIONS请求的响应,该请求涵盖了所有所需的标头。-1因为我不能用这个答案弄清楚该怎么做,所以必须自己弄清楚。
Ed Sykes 2013年

2
我知道2年后,但是... OP多次引用GET请求(我加了强调):«[...]发送到资产文件的请求是OPTIONS请求(应该是GET请求)。» «浏览器试图找出是否“允许” 在执行GET请求之前先下载资产”。那么怎么可能不是AngularJS错误呢?难道不应该禁止GET的预检请求?
polettix

4
如果使用自定义标头,则@polettix甚至GET请求也可以在浏览器中触发飞行前请求。在我链接的文章中检查“不太简单的请求”:html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request。再一次,它是浏览器的机制,而不是AngularJS。
pkozlowski.opensource

70

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

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

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

确保为子文件夹添加**。这是适用于1.2的jsbin: http


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

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

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


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

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

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


这对我有用。这是使用nodejs / express进行服务器端处理的合适方法:gist.github.com/dirkk0/5967221
dirkk0 2013年

14
这仅适用于GET请求,仍无法在跨域上找到POST请求的解决方案。
Pnct

2
将此答案与上面的user2304582答案结合使用应该适用于POST请求。您必须告诉您的服务器接受来自发送该请求的外部服务器的POST请求
Charlie Martin

看来它对我自己不会起作用...所以为-1。在飞行前检查中,您需要在相同的URL上响应OPTIONS请求的内容。您能解释为什么这行得通吗?
Ed Sykes 2013年

1
@EdSykes,我更新了1.2的答案并添加了一个有效的jsbin示例。希望那应该为您解决。确保按下“使用JS运行”按钮。
JStark

62

注意:不确定它是否可以与最新版本的Angular一起使用。

原版的:

也可以覆盖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 = {};
}]);

1
Chrome,FF和IE 10对我来说运行良好。IE9及以下版本应在Windows 7或XP计算机上进行本地测试。
DOM

3
或者,可以仅在$ resource的方法上而不是在全局上设置此方法:$resource('http://yourserver/yourentity/:id', {}, {query: {method: 'GET'});
h7r 2014年

3
有什么收获吗?看起来很简单,但是答案在线程中如此深入?编辑:根本没有工作。
Sebastialonso

此代码在哪里?在app.js,controller.js或确切位置。
Murlidhar Fichadia '16

此代码对获取请求没有任何作用。我在配置中也添加了单独的获取规则
kushalvm '16

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


1
正如我在另一个答案中提到的那样,要详细说明[来自请求的相同ACCESS-CONTROL-REQUEST-HEADERS]部分,您需要查看浏览器要添加的OPTIONS请求。然后,将这些标头添加到您正在构建的服务(或Web服务器)上。在expressjs中,如下所示:ejs.options(' ',function(request,response){response.header('Access-Control-Allow-Origin',' '); response.header('Access-Control-Allow-Methods ','GET,PUT,POST,DELETE'); response.header('Access-Control-Allow-Headers','Content-Type,Authorization,accept,x-requested-with'); response.send(); });
Ed Sykes 2013年

1
Ed Sykes的评论非常准确,请注意,在OPTIONS响应上发送的标头与在POST响应上发送的标头应完全相同,例如:Access-Control-Allow-Origin:*与Access-不相同Control-Allow-Origin:*由于空格。
露西亚2014年

20

同一份文件说

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

它使用GET或POST以外的方法。同样,如果POST用于发送请求类型为application / x-www-form-urlencoded,multipart / form-data或text / plain之外的Content-Type的请求数据,例如POST请求将XML有效负载发送给服务器使用application / xml或text / xml,则对请求进行预检。

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

当原始请求为“ Get”且没有自定义标头时,浏览器不应发出“ Options”请求,而它现在会这样做。问题在于它生成了一个标头X-Requested-With,它强制执行Options请求。有关如何删除此标头的信息,请参见https://github.com/angular/angular.js/pull/1454


10

这解决了我的问题:

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

10

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

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

app.use(cors());

然后你可以做一个npm update


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(...)

参考: 在ASP.NET Web API 2中启用跨域请求


2

我以某种方式通过更改来修复它

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

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

0

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

  • Web Api服务器的解决方案是在配置方法的开头添加对OPTIONS请求的处理(此博客文章中的更多信息):

    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();
    });

以上对我不起作用。有一个更简单的解决方案,可以使CORS与ASP.NET WEB API 2.2及更高版本一起用于AngularJS。从Nuget获取Microsoft WebAPI CORS软件包,然后在您的WebAPI配置文件中... var cors = new EnableCorsAttribute(“ www.example.com”,“ ”,“ ”); config.EnableCors(cors); 细节上微软的网站在这里asp.net/web-api/overview/security/...
kmcnamee

0

如果您将Jersey用作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>

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) {
      ....
   });

我敢肯定有一种更简单/更通用的方式来做到这一点,但是生活太短了...

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


0

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

<system.webServer>节点下的web.config

    <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,它自动添加了相关的标题信息。希望这可以帮助处于类似情况的人。


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正确对话。

希望这对某人有帮助。

干杯。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.