我正在尝试将AngularJS设置为与跨源资源进行通信,其中传递我的模板文件的资产主机位于不同的域上,因此角度执行的XHR请求必须是跨域的。我已经为我的服务器添加了适当的CORS标头,以便使其工作,但它似乎不起作用。问题是,当我在浏览器(chrome)中检查HTTP请求时,发送到资产文件的请求是OPTIONS请求(它应该是GET请求)。
我不确定这是否是AngularJS中的错误,或者我是否需要配置某些内容。根据我的理解,XHR包装器无法发出OPTIONS HTTP请求,因此看起来浏览器正在尝试确定在执行GET请求之前是否“允许”首先下载资产。如果是这种情况,那么我是否还需要为资产主机设置CORS标头(Access-Control-Allow-Origin:http://asset.host..。)?
答案 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(...)
答案 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-Origin
和Access-Control-Allow-Headers
粘贴到这里,仅仅是因为您需要正确设置所有这些内容,才能使Angular App与您的API正确对话。
希望这对某人有帮助。
干杯。