2012-08-24 100 views
242

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

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

回答

206

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

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

很难提供适用于所有WWW服务器的通用解决方案,因为安装程序会根据服务器本身和您打算支持的HTTP动词而有所不同。我会鼓励你克服这个优秀的文章(http://www.html5rocks.com/en/tutorials/cors/),它有关服务器需要发送的确切标题的更多细节。

+1

是的,这是我认为正在发生的事情。原来,我需要在资产主机上设置访问标题才能使其正常工作。 – matsko

+22

@matsko你能详细说明你做了什么来解决这个问题吗?我面临着同样的问题,即AngularJS'$ resource' _POST_请求正在为我的后端ExpressJS服务器(在同一主机上,但是不同的端口上)生成一个_OPTIONS_请求。 – dbau

+0

我使用它的方式是在源服务器和远程请求所针对的任何服务器上都设置CORS头。在这种情况下,您需要为提供JavaScript代码的服务器进行设置,然后对提供$资源数据的服务器执行相同的操作。 – matsko

18

相同的文件说

不同于简单的请求(上面讨论的),“预检”请求第一发送一个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而没有自定义标题时,浏览器不应该进行Options请求。问题是它会生成一个头X-Requested-With强制执行请求。请参阅如何删除这个头https://github.com/angular/angular.js/pull/1454

30

你的服务必须回答与头部的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] 

这里是一个很好的文档:​​

+1

为了详细说明[请求中的相同访问控制请求标题]部分,正如我在另一个答案中提到的,您需要查看浏览器添加的OPTIONS请求。然后,在您正在构建的服务(或Web服务器)上添加这些标头。在expressjs中看起来像:ejs。选项('*',函数(请求,响应){ \t response.header('Access-Control-Allow-Origin','*'); response.header('Access-Control-Allow-Methods' ('Access-Control-Allow-Headers','Content-Type,Authorization,accept,x-requested-with'); response.send(); }); –

+1

Ed Sykes的评论非常准确,请注意在OPTIONS响应中发送的标题和在POST响应中发送的标题应该完全相同,例如:Access-Control-Allow-Origin:*与Access不同-Control-Allow-Origin:*因为空格。 – Lucia

64

角形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版,角1.2.0rc1的解决方案是这样的:

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

这里是jsbin例如它适用于1.2.0rc1: http://jsbin.com/olavok/144/edit


前1.2:对于旧版本(REF 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

+0

这对我有用。以下是使用nodejs/express服务器端的合适魔法:https://gist.github.com/dirkk0/5967221 – dirkk0

+14

这只适用于GET请求,仍然无法找到跨域域的POST请求解决方案。 – Pnct

+2

将以上答案与user2304582答案结合起来应该适用于POST请求。你必须告诉你的服务器接受来自发送它的外部服务器的POST请求 –

56

注意:不确定它适用于最新版本的Angular。

ORIGINAL:

它也可以覆盖OPTIONS请求(仅在浏览器测试):

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适合我。对于IE 9及更低版本,应该在Windows 7或XP机器上进行本机测试。 – DOM

+3

或者,可以单独设置该资源的方法而不是全局设置:'$ resource('http:// yourserver/yourentity /:id',{},{query:{method:'GET'});' – h7r

+2

感谢这宝贵的信息。 – Sudhakar

0

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

  • 解决方案的Web API服务器是在配置方法的开始(更多信息in this blog post)添加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(); 
    }); 
    
+0

以上不适合我。有一个更简单的解决方案来启用CORS,以便与ASP.NET WEB API 2.2及更高版本一起使用AngularJS。从Nuget获取Microsoft WebAPI CORS包,然后在WebAPI配置文件中获取... var cors = new EnableCorsAttribute(“www.example.com”,“*”,“*”); config.EnableCors(cors);详情请访问Microsoft网站http://www.asp.net/web-api/overview/security/enabling-cross-origin-requests-in-web-api – kmcnamee

0

如果您使用的球衣REST API的你可以做如下

你不必改变你的web服务实现。

我将在web.xml为泽西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)然后,在球衣的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; 
     } 
    } 
} 

在我的角度控制器...

$http.get("/Handlers/LoadJSONData.ashx") 
    .success(function (data) { 
     .... 
    }); 

我敢肯定有这样做的一个简单/更通用的方法,但生活太短...

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

8

这个固定我的问题:

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

这也适用于我。 – Mathdesigner47

+0

解决了我的问题! 10x – Fabiano

+0

你救了我男孩 – FewFlyBy

1

不知怎的,我通过改变

访问控制允许报头固定它, “源,X-要求-着,内容类型,接受,授权”

访问控制允许报头“的由来,内容类型,接受,授权”

3

这里是我固定在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); 
        } 
    } 
    
  • 我能够通过这种方式

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

参考JSON发送到行动: Enabling Cross-Origin Requests in ASP.NET Web API 2

7

如果您使用的是服务器的NodeJS,你可以使用这个库,这对我https://github.com/expressjs/cors

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

app.use(cors()); 

之后你可以做一个npm update工作的罚款。