AngularJS 对跨域资源执行 OPTIONS HTTP 请求
Posted
技术标签:
【中文标题】AngularJS 对跨域资源执行 OPTIONS HTTP 请求【英文标题】:AngularJS performs an OPTIONS HTTP request for a cross-origin resource 【发布时间】:2012-08-20 04:01:21 【问题描述】:我正在尝试设置 AngularJS 以与跨域资源通信,其中提供我的模板文件的资产主机位于不同的域中,因此 Angular 执行的 XHR 请求必须是跨域的。我已经为 HTTP 请求添加了适当的 CORS 标头到我的服务器以使其工作,但它似乎不起作用。问题是当我在浏览器 (chrome) 中检查 HTTP 请求时,发送到资产文件的请求是一个 OPTIONS 请求(它应该是一个 GET 请求)。
我不确定这是否是 AngularJS 中的错误,或者我是否需要配置一些东西。据我了解,XHR 包装器无法发出 OPTIONS HTTP 请求,因此看起来浏览器正在尝试确定是否“允许”在执行 GET 请求之前先下载资产。如果是这种情况,那么我是否还需要使用资产主机设置 CORS 标头(Access-Control-Allow-Origin: http://asset.host...)?
【问题讨论】:
【参考方案1】:OPTIONS 请求绝不是 AngularJS 错误,这是跨域资源共享标准要求浏览器的行为方式。请参阅此文档:https://developer.mozilla.org/en-US/docs/HTTP_access_control,在“概述”部分中它说:
跨域资源共享标准通过添加新的 HTTP 来工作 允许服务器描述源集的标头 允许使用网络浏览器阅读该信息。此外, 对于可能对用户数据造成副作用的 HTTP 请求方法(在 特定;对于 GET 以外的 HTTP 方法,或对于 POST 使用 某些 MIME 类型)。该规范要求浏览器 “预检”请求,从服务器请求支持的方法 带有 HTTP OPTIONS 请求标头,然后在“批准”时 服务器,使用实际的 HTTP 请求发送实际的请求 方法。服务器还可以通知客户端是否“凭据” (包括 Cookie 和 HTTP 身份验证数据)应与 请求。
很难提供适用于所有 WWW 服务器的通用解决方案,因为设置会因服务器本身和您打算支持的 HTTP 动词而异。我鼓励您阅读这篇出色的文章 (http://www.html5rocks.com/en/tutorials/cors/),其中包含有关需要由服务器发送的确切标头的更多详细信息。
【讨论】:
@matsko 你能详细说明你做了什么来解决这个问题吗?我面临同样的问题,AngularJS$resource
POST 请求正在向我的后端 ExpressJS 服务器生成一个 OPTIONS 请求(在同一主机上;但端口不同) .
对于所有反对者——几乎不可能为所有网络服务器提供准确配置设置——在这种情况下,答案需要 10 页。相反,我链接到提供更多详细信息的文章。
您是对的,您无法给出答案 - 您需要在 OPTIONS 响应中添加标题,该响应涵盖浏览器请求的所有标题,在我的情况下,使用 chrome标题“接受”和“x-requested-with”。在 chrome 中,我通过查看网络请求并查看 chrome 要求的内容来确定需要添加的内容。我使用 nodejs/expressjs 作为支持,因此我创建了一个服务,该服务返回对 OPTIONS 请求的响应,该请求涵盖了所需的所有标头。 -1 因为我无法使用这个答案来弄清楚该怎么做,必须自己弄清楚。
我知道它是 2 年多以后,但是...... OP 多次引用 GET 请求(强调由我添加):«[...] 发送到资产文件的请求是OPTIONS 请求(应该是 GET 请求)。»并且«浏览器试图确定是否“允许”首先下载资产在它执行 GET 请求之前»。那么它怎么可能不是 AngularJS 的错误呢? GET不应该禁止预检请求吗?
@polettix 如果使用自定义标头,即使 GET 请求在浏览器中也可以触发飞行前请求。检查我链接的文章中的“不那么简单的请求”:html5rocks.com/en/tutorials/cors/#toc-making-a-cors-request。再一次,它是浏览器的机制,而不是AngularJS。【参考方案2】:
对于 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
【讨论】:
这对我有用。这是使用 nodejs/express 的服务器端的适当魔法:gist.github.com/dirkk0/5967221 这仅适用于GET请求,仍然无法找到跨域POST请求的解决方案。 将此答案与上面的 user2304582 答案结合起来应该适用于 POST 请求。您必须告诉您的服务器接受来自发送它的外部服务器的 POST 请求 这对我来说似乎不会自行工作......所以是-1。作为飞行前检查的一部分,您需要在同一 URL 上响应 OPTIONS 请求的内容。你能解释一下为什么会这样吗? @EdSykes ,我已经更新了 1.2 的答案并添加了一个有效的 jsbin 示例。希望这应该为您解决。确保按下“使用 JS 运行”按钮。【参考方案3】:注意:不确定它是否适用于最新版本的 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 = ;
]);
【讨论】:
在 Chrome、FF 和 IE 10 上运行良好。IE 9 及更低版本应在 Windows 7 或 XP 机器上进行本机测试。 或者,可以单独在 $resource 的方法上而不是全局上设置它:$resource('http://yourserver/yourentity/:id', , query: method: 'GET');
这有什么问题吗?看起来很简单,但答案却如此深入人心?编辑:根本没用。
这段代码去哪儿了?在 app.js、controller.js 或确切的位置。
此代码对获取请求没有任何作用。我在我的配置中添加了单独的 get 规则【参考方案4】:
您的服务必须回答带有以下标头的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
【讨论】:
要详细说明 [the same ACCESS-CONTROL-REQUEST-HEADERS from request] 部分,正如我在另一个答案中提到的,您需要查看浏览器正在添加的 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 的评论非常准确,注意 OPTIONS 响应中发送的 headers 和 POST 响应中发送的 headers 应该完全相同,例如:Access-Control-Allow-Origin: * 与 Access-Control-Allow-Origin 不同:* 因为有空格。【参考方案5】:同一份文件说
与简单请求(上面讨论过)不同,“预检”请求首先向另一个域上的资源发送一个 HTTP OPTIONS 请求标头,以确定实际请求是否可以安全发送。跨站点请求是这样预检的,因为它们可能对用户数据有影响。特别是,在以下情况下会预检请求:
它使用 GET 或 POST 以外的方法。此外,如果 POST 用于发送 Content-Type 不是 application/x-www-form-urlencoded、multipart/form-data 或 text/plain 的请求数据,例如如果 POST 请求使用 application/xml 或 text/xml 将 XML 有效负载发送到服务器,则请求被预检。
它在请求中设置自定义标头(例如,请求使用诸如 X-PINGOTHER 之类的标头)
当原始请求是没有自定义标头的 Get 时,浏览器不应像现在那样发出 Options 请求。问题是它会生成一个标题 X-Requested-With 来强制选项请求。请参阅https://github.com/angular/angular.js/pull/1454 了解如何删除此标头
【讨论】:
【参考方案6】:这解决了我的问题:
$http.defaults.headers.post["Content-Type"] = "text/plain";
【讨论】:
【参考方案7】:如果你使用的是 nodeJS 服务器,你可以使用这个库,它对我来说很好https://github.com/expressjs/cors
var express = require('express')
, cors = require('cors')
, app = express();
app.use(cors());
然后你可以做一个npm update
。
【讨论】:
【参考方案8】:这是我在 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
【讨论】:
【参考方案9】:不知何故,我通过更改修复了它
<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"
/>
【讨论】:
【参考方案10】:在 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();
);
【讨论】:
以上对我不起作用。有一个更简单的解决方案可以让 CORS 与 AngularJS 和 ASP.NET WEB API 2.2 及更高版本一起使用。从 Nuget 获取 Microsoft WebAPI CORS 包,然后在您的 WebAPI 配置文件中... var cors = new EnableCorsAttribute("www.example.com", "", ""); config.EnableCors(cors);详细信息在微软网站上asp.net/web-api/overview/security/…【参考方案11】:如果您将 Jersey 用于 REST API,您可以执行以下操作
您不必更改您的网络服务实现。
我会解释 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>
【讨论】:
【参考方案12】:我放弃了尝试解决这个问题。
我的 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)
....
);
我确信有一种更简单/更通用的方法可以做到这一点,但是生命太短了......
这对我有用,我现在可以继续正常工作了!!
【讨论】:
【参考方案13】:对于带有 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,它自动添加了相关的标头信息。希望这可以帮助遇到类似情况的人。
【讨论】:
【参考方案14】:晚会有点晚了,
如果您使用 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-Origin
和 Access-Control-Allow-Headers
,只是因为您需要正确设置所有这些,以便 Angular App 正确地与您的 API 对话。
希望这对某人有所帮助。
干杯。
【讨论】:
以上是关于AngularJS 对跨域资源执行 OPTIONS HTTP 请求的主要内容,如果未能解决你的问题,请参考以下文章
AngularJS 对跨域资源执行 OPTIONS HTTP 请求 => 错误 401