267

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

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

4

14 回答 14

228

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/ ),其中包含有关需要由服务器发送的确切标头的更多详细信息。

于 2012-08-24T15:23:56.413 回答
70

对于 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/]);
 }])

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


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

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

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

于 2013-05-15T16:40:51.857 回答
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 = {};
}]);
于 2013-12-11T10:07:42.710 回答
34

Your service must answer an OPTIONS request with headers like these:

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]

Here is a good doc: http://www.html5rocks.com/en/tutorials/cors/#toc-adding-cors-support-to-the-server

于 2013-04-21T14:51:24.653 回答
20

同一份文件说

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

它使用 GET 或 POST 以外的方法。此外,如果 POST 用于发送 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 的请求数据,例如,如果 POST 请求向服务器发送 XML 有效负载使用 application/xml 或 text/xml,然后预检请求。

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

当原始请求是没有自定义标头的 Get 时,浏览器不应像现在那样发出 Options 请求。问题是它会生成一个标题 X-Requested-With 来强制选项请求。有关如何删除此标头的信息,请参见https://github.com/angular/angular.js/pull/1454

于 2013-03-09T02:45:37.380 回答
10

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

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

app.use(cors());

然后你可以做一个npm update.

于 2016-10-14T17:11:17.603 回答
10

这解决了我的问题:

$http.defaults.headers.post["Content-Type"] = "text/plain";
于 2016-02-25T12:59:46.653 回答
5

这是我在 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 中启用跨域请求

于 2016-06-23T14:45:31.720 回答
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" 
     />
于 2016-03-14T15:21:49.427 回答
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();
    });
    
于 2015-02-27T08:38:57.557 回答
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>
于 2015-05-26T06:29:25.870 回答
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>

全局.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,它自动添加了相关的标头信息。希望这可以帮助遇到类似情况的人。

于 2018-11-01T20:52:31.217 回答
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) {
      ....
   });

我敢肯定有一种更简单/更通用的方法可以做到这一点,但生命太短暂了......

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

于 2015-11-11T12:42:33.493 回答
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-Originand Access-Control-Allow-Headers,仅仅是因为您需要正确设置所有这些,以便 Angular App 正确地与您的 API 对话。

希望这可以帮助某人。

干杯。

于 2019-01-24T15:44:52.747 回答