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

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

我不确定这是AngularJS中的一个bug,还是我需要配置一些东西。根据我的理解,XHR包装器不能做出一个OPTIONS HTTP请求,所以看起来就像浏览器在执行GET请求之前试图弄清楚是否“允许”首先下载资产。如果是这种情况,那么我是否需要设置CORS头(Access-Control-Allow-Origin: http://asset.host.。.)与资产主机?

274556 次浏览

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

跨起源资源共享标准通过添加新的HTTP来工作 头,允许服务器描述源的集合 允许使用网络浏览器读取该信息。此外, 用于可能对用户数据产生副作用的HTTP请求方法(在 特定的;用于GET以外的HTTP方法,或用于POST的使用 某些MIME类型)。规范要求浏览器 “preflight”请求,从服务器请求支持的方法 使用HTTP OPTIONS请求头,然后,在“批准”从 服务器,发送实际请求和实际HTTP请求 方法。服务器还可以通知客户端是否“凭据” (包括Cookies和HTTP身份验证数据) 请求。< / p >

提供一个适用于所有WWW服务器的通用解决方案是非常困难的,因为设置取决于服务器本身和你打算支持的HTTP动词。我鼓励你阅读这篇优秀的文章(http://www.html5rocks.com/en/tutorials/cors/),它有更多关于服务器需要发送的确切头部的详细信息。

同一份文件说

不像简单的请求(上面讨论过),&;preflted& quot;请求首先向另一个域上的资源发送一个HTTP OPTIONS请求头,以确定实际的请求发送是否安全。跨站点请求就像这样被预先传送,因为它们可能会对用户数据产生影响。特别是,如果出现以下情况,请求将被预飞:

它使用的方法不是GET或POST。同样,如果POST用于发送内容类型不是application/x-www-form-urlencoded、multipart/form-data或text/plain的请求数据,例如,如果POST请求使用application/ XML或text/ XML向服务器发送XML有效负载,则该请求是预飞的。

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

当原始请求是没有自定义头的Get请求时,浏览器不应该像现在这样发出Options请求。问题是它生成了一个头部X-Requested-With,它强制Options请求。关于如何删除这个头文件,请参阅https://github.com/angular/angular.js/pull/1454

你的服务必须用这样的头来回答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

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

1.2:发布版本他们添加了一个escapeForRegexp函数,所以你不再需要转义字符串。你可以直接添加url

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

确保子文件夹添加**。下面是一个适用于1.2版本的jsbin: http://jsbin.com/olavok/145/edit < / p >


1.2.0rc:如果你仍然使用rc版本,Angular 1.2.0rc1的解决方案是这样的:

.config(['$sceDelegateProvider', function($sceDelegateProvider) {
$sceDelegateProvider.resourceUrlWhitelist(['self', /^https?:\/\/(cdn\.)?yourdomain.com/]);
}])
下面是一个jsbin的例子,它适用于1.2 rc1: http://jsbin.com/olavok/144/edit < / p >

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

$httpProvider.defaults.useXDomain = true;
delete $httpProvider.defaults.headers.common['X-Requested-With'];
下面是一个jsbin的例子,它适用于1.2之前的版本: http://jsbin.com/olavok/11/edit < / p >

注意:不确定它是否适用于最新版本的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 = {};
}]);
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();
    });
    

如果你正在使用Jersey的REST API,你可以这样做

你不需要改变你的webservices实现。

我会解释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>

我放弃了解决这个问题。

我的IIS网络。config中有相关的“Access-Control-Allow-Methods”,我尝试在我的Angular代码中添加配置设置,但在花了几个小时试图让Chrome调用一个跨域JSON web服务后,我悲惨地放弃了。

最后,我添加了一个愚蠢的ASP。Net处理程序网页,得到调用我的JSON web服务,并返回结果。它在两分钟内就运行起来了。

下面是我使用的代码:

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

我相信有一种更简单/更通用的方式来做这件事,但生命太短暂了…

这对我很有效,现在我可以继续做正常的工作了!!

这解决了我的问题:

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

不知怎的,我通过改变来解决它

<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"
/>

以下是我在ASP上修复这个问题的方法。网

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

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

    public static class WebApiConfig
    {
    public static void Register(HttpConfiguration config)
    {
    config.EnableCors();
    
    
    ...
    }
    }
    
  • Add this attribute on your controller class

    [EnableCors(origins: "*", headers: "*", methods: "*")]
    public class MyController : ApiController
    {
    [AcceptVerbs("POST")]
    public IHttpActionResult Post([FromBody]YourDataType data)
    {
    ...
    return Ok(result);
    }
    }
    
  • I was able to send json to the action by this way

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

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

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

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


app.use(cors());

之后你可以做npm update

对于一个带有API的IIS MVC 5 / Angular CLI(是的,我很清楚你的问题是Angular JS)项目,我做了以下工作:

<system.webServer>节点下的. 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,它会自动添加相关的头信息。希望这能帮助有类似情况的人。

来的有点晚了

如果你正在使用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对话。

希望这能帮助到一些人。

欢呼。