如何在AngularJs中启用CORS


147

我已经使用JavaScript为Flickr照片搜索API创建了一个演示。现在,我将其转换为AngularJs。我在互联网上搜索,发现下面的配置。

组态:

myApp.config(function($httpProvider) {
  $httpProvider.defaults.useXDomain = true;
  delete $httpProvider.defaults.headers.common['X-Requested-With'];
});

服务:

myApp.service('dataService', function($http) {
    delete $http.defaults.headers.common['X-Requested-With'];
    this.flickrPhotoSearch = function() {
        return $http({
            method: 'GET',
            url: 'http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=3f807259749363aaa29c76012fa93945&tags=india&format=json&callback=?',
            dataType: 'jsonp',
            headers: {'Authorization': 'Token token=xxxxYYYYZzzz'}
         });
     }
});

控制器:

myApp.controller('flickrController', function($scope, dataService) {
        $scope.data = null;
        dataService.flickrPhotoSearch().then(function(dataResponse) {
            $scope.data = dataResponse;
            console.log($scope.data);
        });
    });

但是我仍然遇到同样的错误。这是我尝试过的一些链接:

XMLHttpRequest无法加载URL。Access-Control-Allow-Origin不允许的来源

http://goo.gl/JuS5B1


1
您必须从代理请求数据,而您仍然直接从flickr请求数据。
昆汀2014年

@quentin感谢您的快速回复。能否请给我一个演示。
ankitr 2014年

1
您只需将URL从flickr.com更改为代理的URL
Quentin 2014年

1
但是我怎么称呼flickr?因为我需要来自flickr的图像。
ankitr 2014年

2
客户端调用代理。代理调用flickr。那就是代理的意思。(您的代理代码…不是代理,而是用于从静态文件中提供JSON和JSONP的Web服务器)。
昆汀2014年

Answers:


193

你不知道 您所请求的服务器必须实现CORS才能从您的网站访问权限中授予JavaScript。您的JavaScript无法授予自己访问其他网站的权限。


1
而是从您的服务器向Flickr发出请求。
昆汀

我没有使用服务器,但是可以使用node.js。你能告诉我这个方法吗?
ankitr 2014年

1
注释中没有可用空间。这是一组很大的主题。
昆汀

为什么,我可以从一个响应https://www.google.com使用像邮差的应用程序,但是当我尝试GEThttps://www.google.com使用AJAX调用我得到的CORS错误?我无法使AJAX调用的行为类似于POSTMAN的调用吗?
AjaxLeung

5
@AlexLeung —邮递员是已安装的应用程序。如果您的网站可以让我的浏览器从Google请求数据并读取它,那么您的网站可以请求我的GMail收件箱页面并阅读我的所有电子邮件。那太可怕了。
昆汀

64

我有一个类似的问题,对我来说,归结为在接收端响应中添加以下HTTP标头:

Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Origin: *

您可能不希望*在末尾使用,而仅使用发送数据的主机的域名。喜欢*.example.com

但这仅在您有权访问服务器的配置时才可行。


2
我是AngularJs的新手。请您告诉我在哪里实现?
ankitr 2014年

17
@Ankit您需要在服务器中而不是AngularJS中添加这些标头。
菲利佩·阿尔梅达

2
@techcraver-您不需要对服务器配置的操作权限-只需从脚本内传递标头即可。如果您有PHP后端,它将是header('Access-Control-Allow-Origin: *');
davidkonrad,2016年

@davidkonrad:我已经在angular v4中创建了整个应用程序。你能告诉我我必须在哪里包含这些标题

@Pygirl,我相信您已经使客户端成为了有角度的人?您需要将标头添加到响应服务器端,方式和位置取决于技术。如果您是从某个角度Http.get()或类似角度调用的PHP脚本,则将它header('Access-Control-Allow-Origin: *')作为被调用的PHP脚本的第一个输出添加(即,在输出实际响应JSON之前,无论如何。)
davidkonrad

9

尝试使用资源服务来消耗flickr jsonp:

var MyApp = angular.module('MyApp', ['ng', 'ngResource']);

MyApp.factory('flickrPhotos', function ($resource) {
    return $resource('http://api.flickr.com/services/feeds/photos_public.gne', { format: 'json', jsoncallback: 'JSON_CALLBACK' }, { 'load': { 'method': 'JSONP' } });
});

MyApp.directive('masonry', function ($parse) {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.masonry({ itemSelector: '.masonry-item', columnWidth: $parse(attrs.masonry)(scope) });
        }
    };        
});

MyApp.directive('masonryItem', function () {
    return {
        restrict: 'AC',
        link: function (scope, elem, attrs) {
            elem.imagesLoaded(function () {
               elem.parents('.masonry').masonry('reload');
            });
        }
    };        
});

MyApp.controller('MasonryCtrl', function ($scope, flickrPhotos) {
    $scope.photos = flickrPhotos.load({ tags: 'dogs' });
});

模板:

<div class="masonry: 240;" ng-controller="MasonryCtrl">
    <div class="masonry-item" ng-repeat="item in photos.items">
        <img ng-src="{{ item.media.m }}" />
    </div>
</div>

4

发生此问题的原因是,Web应用程序安全模型策略是“ 相同来源策略”。根据该策略,Web浏览器允许第一个网页中包含的脚本访问第二个网页中的数据,但前提是两个网页具有相同的来源。这意味着请求者必须匹配请求站点的确切主机,协议和端口。

我们有多种选择可以解决这个CORS标头问题。

  1. 使用代理 -在此解决方案中,我们将运行代理,以便当请求通过代理时,它看起来就像是某个相同的来源。如果您使用的是nodeJS,则可以在任何地方使用cors做代理工作。https://www.npmjs.com/package/cors-anywhere

    例子:-

    var host = process.env.HOST || '0.0.0.0';
    var port = process.env.PORT || 8080;
    var cors_proxy = require('cors-anywhere');
    cors_proxy.createServer({
        originWhitelist: [], // Allow all origins
        requireHeader: ['origin', 'x-requested-with'],
        removeHeaders: ['cookie', 'cookie2']
    }).listen(port, host, function() {
        console.log('Running CORS Anywhere on ' + host + ':' + port);
    });
  2. JSONP -JSONP是一种发送JSON数据而无需担心跨域问题的方法,它不使用XMLHttpRequest对象,<script>而是使用标记。https://www.w3schools.com/js/js_json_jsonp.asp

  3. 服务器端 -在服务器端,我们需要启用跨域请求。首先,我们将获得预检请求(OPTIONS),我们需要允许状态代码为200(ok)的请求。

    预检请求首先将HTTP OPTIONS请求标头发送到另一个域上的资源,以确定实际请求是否可以安全发送。跨站点请求这样被预检,因为它们可能会影响用户数据。特别是,如果请求使用GET或POST以外的方法,则该请求将被预检。另外,如果POST用于发送请求类型为application / x-www-form-urlencoded,multipart / form-data或text / plain以外的Content-Type的请求数据,例如POST请求将XML有效负载发送给服务器使用application / xml或text / xml,则对请求进行预检。它在请求中设置自定义标头(例如,请求使用标头,例如X-PINGOTHER)

    如果您使用弹簧,只需添加以下代码即可解决该问题。在这里,我已经禁用了csrf令牌,该令牌可以根据您的要求启用/禁用。

    @SpringBootApplication
    public class SupplierServicesApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(SupplierServicesApplication.class, args);
        }
    
        @Bean
        public WebMvcConfigurer corsConfigurer() {
            return new WebMvcConfigurerAdapter() {
                @Override
                public void addCorsMappings(CorsRegistry registry) {
                    registry.addMapping("/**").allowedOrigins("*");
                }
            };
        }
    }

    如果您使用的是Spring Security,请同时使用以下代码和上述代码。

    @Configuration
    @EnableWebSecurity
    public class SupplierSecurityConfig extends WebSecurityConfigurerAdapter {
    
        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http.csrf().disable().authorizeRequests().antMatchers(HttpMethod.OPTIONS, "/**").permitAll().antMatchers("/**").authenticated().and()
                    .httpBasic();
        }
    
    }

2

我遇到了类似的问题,后端存在问题。我正在使用节点服务器(快速)。我有一个从前端(角度)的获取请求,如下所示

   onGetUser(){
        return this.http.get("http://localhost:3000/user").pipe(map(
            (response:Response)=>{
                const user =response.json();
                return user;
            }
        )) 
    }

但是它给出了以下错误错误

这是使用express编写的没有头的后端代码

app.get('/user',async(req,res)=>{
     const user=await getuser();
     res.send(user);
 })

将标题添加到方法后,问题得以解决

app.get('/user',async(req,res)=>{
    res.header("Access-Control-Allow-Origin", "*");
    const user=await getuser();
    res.send(user);
})

您可以获取有关在Node JS上启用CORS的更多详细信息


1

我自己回答。

POST上的CORS angular js + restEasy

好吧,最后我来到了这种解决方法:它与IE一起工作的原因是因为IE直接发送POST而不是首先发送预检请求以请求许可。但是我仍然不知道为什么过滤器无法管理OPTIONS请求,并默认发送过滤器中未描述的标头(似乎只是对这种情况的替代...也许是restEasy的事情.. )

因此,我在我的rest服务中创建了一个OPTIONS路径,该路径重写了响应并使用响应标头将标头包含在响应中

如果有人以前遇到过这种情况,我仍在寻找一种干净的方法。


1

在大多数企业中,或者您在家中使用Centos / etc时,Apache / HTTPD往往会出现。因此,如果有这种情况,您可以非常轻松地执行代理以添加必要的CORS标头。

我在这里有一篇博客文章,因为最近我遭受了很多次。但是重要的一点只是将其添加到您的/etc/httpd/conf/httpd.conf文件中,并确保您已经在执行“ Listen 80”:

<VirtualHost *:80>
    <LocationMatch "/SomePath">
       ProxyPass http://target-ip:8080/SomePath
       Header add "Access-Control-Allow-Origin" "*"
    </LocationMatch>
</VirtualHost>

这样可以确保所有对your-server-ip:80 / SomePath下的URL的请求都路由到http:// target-ip:8080 / SomePath(不具有CORS支持的API),并确保它们返回正确的Access-Control-Allow- Origin标头,使他们可以与您的网络应用一起使用。

当然,您可以根据需要更改端口并以整个服务器为目标,而不是SomePath。


1

此答案概述了解决不支持CORS的API的两种方法:

  • 使用CORS代理
  • 如果API支持,请使用JSONP

一种解决方法是使用CORS PROXY:

angular.module("app",[])
.run(function($rootScope,$http) { 
     var proxy = "//cors-anywhere.herokuapp.com";
     var url = "http://api.ipify.org/?format=json";
     $http.get(proxy +'/'+ url)
       .then(function(response) {
         $rootScope.response = response.data;
     }).catch(function(response) {
         $rootScope.response = 'ERROR: ' + response.status;
     })     
})
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app">
   Response = {{response}}
</body>

有关更多信息,请参见


如果API支持,请使用JSONP:

 var url = "//api.ipify.org/";
 var trust = $sce.trustAsResourceUrl(url);
 $http.jsonp(trust,{params: {format:'jsonp'}})
   .then(function(response) {
     console.log(response);
     $scope.response = response.data;
 }).catch(function(response) {
     console.log(response);
     $scope.response = 'ERROR: ' + response.status;
 }) 

PLNKR上演示

有关更多信息,请参见


0
        var result=[];
        var app = angular.module('app', []);
        app.controller('myCtrl', function ($scope, $http) {
             var url="";// your request url    
             var request={};// your request parameters
             var headers = {
             // 'Authorization': 'Basic ' + btoa(username + ":" + password),
            'Access-Control-Allow-Origin': true,
            'Content-Type': 'application/json; charset=utf-8',
            "X-Requested-With": "XMLHttpRequest"
              }
             $http.post(url, request, {
                        headers
                 })
                 .then(function Success(response) {
                      result.push(response.data);             
                      $scope.Data = result;              
                 }, 
                  function Error(response) {
                      result.push(response.data);
                       $scope.Data = result;
                    console.log(response.statusText + " " + response.status)
               }); 
     });

And also add following code in your WebApiConfig file            
        var cors = new EnableCorsAttribute("*", "*", "*");
        config.EnableCors(cors);

“并且还要在WebApiConfig文件中添加以下代码” —问题是关于向Flickr发出请求。它们的服务器不受OP的控制。Flickr可能也不使用ASP.NET。
昆汀

0

我们可以使用ngResourse模块在前端启用CORS。但最重要的是,在控制器中发出ajax请求时,我们应该拥有这段代码,

$scope.weatherAPI = $resource(YOUR API,
     {callback: "JSON_CALLBACK"}, {get: {method: 'JSONP'}});
 $scope.weatherResult = $scope.weatherAPI.get(YOUR REQUEST DATA, if any);

另外,您必须在脚本部分中添加ngResourse CDN,并在应用程序模块中将其添加为依赖项。

<script src="https://code.angularjs.org/1.2.16/angular-resource.js"></script>

然后在应用模块依赖项部分中使用“ ngResourse”

var routerApp = angular.module("routerApp", ["ui.router", 'ngResource']);

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.