如何在AngularJs中启用CORS

155

我已使用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 cannot load URL. Origin not allowed by Access-Control-Allow-Origin

http://goo.gl/JuS5B1


1
你必须从代理服务器请求数据,否则你仍然是直接从 Flickr 请求数据。 - Quentin
@quentin 感谢您的快速回复。您能给我演示一下吗? - ankitr
1
你只需要将URL从flickr.com更改为代理的URL即可。 - Quentin
1
但是我该如何调用 Flickr 呢?因为我需要从 Flickr 获取图片。 - ankitr
3
客户端调用代理,代理再调用 Flickr。这就是代理的意思。(您的代理代码并不是代理,而是一个用于从静态文件提供 JSON 和 JSONP 的 Web 服务器)。 - Quentin
显示剩余2条评论
10个回答

204

不能。您发起请求的服务器必须实现CORS,以授予来自您网站的JavaScript访问权限。您的JavaScript无法自行授权访问其他网站。


1
请在您的服务器上向Flickr发出请求。 - Quentin
1
不是在评论区内提供空间,而是一系列庞大的主题。 - Quentin
6
@AlexLeung - Postman 是一款需要安装的应用程序。如果您的网站可以让我的浏览器请求谷歌数据并读取它,那么您的网站就可以请求我的 Gmail 收件箱页面并读取我所有的电子邮件。那将是可怕的。 - Quentin
在 Spring 服务器上,只需要在你的控制器中添加 @CrossOrigin(origins = "http://localhost:8080")。 - amdev
您可以使用一个轻量级的反向代理服务器来实现这个目的。 - João Rodrigues
显示剩余3条评论

68

我遇到了类似的问题,最后发现解决方法就是在接收端响应中添加以下HTTP头部:

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

您可以选择不在末尾使用*,而只使用发送数据的主机的域名。比如*.example.com

但是这仅适用于您可以访问服务器配置的情况。


2
我是AngularJs的新手。请问您能告诉我在哪里实现它吗? - ankitr
21
你需要在服务器中添加这些标题,而不是在AngularJS中添加。 - Felipe
2
@techcraver - 你不需要操作服务器配置,只需从脚本内部传递头信息即可。如果你有一个PHP后端,可以使用header('Access-Control-Allow-Origin: *'); - davidkonrad
@davidkonrad:我已经用Angular v4创建了整个应用程序。你能告诉我在哪里包含这些头文件吗?:/ - Pygirl
@Pygirl,我相信你已经用Angular做了客户端?你需要在服务器端添加响应头,具体如何添加取决于技术。如果你是从Angular的Http.get()或类似方法调用PHP脚本,那么请在被调用的PHP脚本中作为第一个输出添加header('Access-Control-Allow-Origin: *')(即在实际响应、JSON等输出之前)。 - davidkonrad
我有Asp.Net MVC和AngularJS,已经在web.config上实现了。但是没有得到响应。 - Abdulla Sirajudeen

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>

6

这个问题出现是因为Web应用程序安全模型策略,即同源策略。根据该策略,只有在两个网页具有相同的来源时,Web浏览器才允许第一个网页中包含的脚本访问第二个网页中的数据。这意味着请求者必须与请求站点的确切主机、协议和端口匹配。

我们有多种选项来解决CORS头问题。

  1. Using Proxy - In this solution we will run a proxy such that when request goes through the proxy it will appear like it is some same origin. If you are using the nodeJS you can use cors-anywhere to do the proxy stuff. https://www.npmjs.com/package/cors-anywhere.

    Example:-

    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 is a method for sending JSON data without worrying about cross-domain issues.It does not use the XMLHttpRequest object.It uses the <script> tag instead. https://www.w3schools.com/js/js_json_jsonp.asp

  3. Server Side - On server side we need to enable cross-origin requests. First we will get the Preflighted requests (OPTIONS) and we need to allow the request that is status code 200 (ok).

    Preflighted requests first send an HTTP OPTIONS request header to the resource on the other domain, in order to determine whether the actual request is safe to send. Cross-site requests are preflighted like this since they may have implications to user data. In particular, a request is preflighted if it uses methods other than GET or POST. Also, if POST is used to send request data with a Content-Type other than application/x-www-form-urlencoded, multipart/form-data, or text/plain, e.g. if the POST request sends an XML payload to the server using application/xml or text/xml, then the request is preflighted. It sets custom headers in the request (e.g. the request uses a header such as X-PINGOTHER)

    If you are using the spring just adding the bellow code will resolves the issue. Here I have disabled the csrf token that doesn't matter enable/disable according to your requirement.

    @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("*");
                }
            };
        }
    }
    

    If you are using the spring security use below code along with above code.

    @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
我遇到了一个类似的问题,问题出在后端。我使用的是Node服务器(Express)。前端(Angular)向后端发起了以下GET请求:Original Answer。
   onGetUser(){
        return this.http.get("http://localhost:3000/user").pipe(map(
            (response:Response)=>{
                const user =response.json();
                return user;
            }
        )) 
    }

"最初的回答":这是使用express编写的后端代码,但缺少头文件,导致出现以下错误:The error
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中获取更多细节。最初的回答。

2

我自己回答了。

CORS angular js + restEasy on POST

最终,我找到了这个解决方法: IE之所以可以工作是因为它直接发送POST请求,而不是首先发送预检请求来请求权限。 但我仍然不知道为什么过滤器无法处理OPTIONS请求,并默认发送未在过滤器中描述的标头(似乎只有这种情况才会覆盖...也许是restEasy的问题...)

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

如果有人遇到过这种情况,我仍在寻找更好的解决方法。


2

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

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

其中一种解决方法是使用CORS代理:

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上的DEMO

更多信息请参见:


1

Apache/HTTPD在大多数企业或者家庭使用Centos等系统时都会存在。因此,如果你有这个环境,你可以很容易地做一个代理来添加必要的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 header,以允许它们与您的 Web 应用程序配合使用。当然,如果您愿意,您可以更改端口并将整个服务器作为目标,而不是 SomePath。

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。 - Quentin

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']);


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接