Comment activer CORS dans AngularJs

147

J'ai créé une démo en utilisant JavaScript pour l'API de recherche de photos Flickr. Maintenant, je le convertis en AngularJs. J'ai cherché sur Internet et trouvé la configuration ci-dessous.

Configuration:

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

Un service:

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

Manette:

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

Mais j'ai toujours la même erreur. Voici quelques liens que j'ai essayés:

XMLHttpRequest ne peut pas charger l'URL. Origine non autorisée par Access-Control-Allow-Origin

http://goo.gl/JuS5B1

Ankitr
la source
1
Vous devez demander les données à votre proxy, vous les demandez toujours directement à flickr.
Quentin
@quentin Merci pour une réponse rapide. Pouvez-vous s'il vous plaît me donner une démo.
ankitr
1
Vous venez de changer l'URL de flickr.com à l'URL de votre proxy
Quentin
1
Mais comment vais-je appeler Flickr? car j'ai besoin des images de flickr.
ankitr
2
Le client appelle le proxy. Le proxy appelle flickr. C'est ce que signifie le proxy. (Votre code proxy… n'est pas un proxy, c'est un serveur Web pour servir JSON et JSONP à partir de fichiers statiques).
Quentin

Réponses:

194

Vous ne le faites pas. Le serveur auquel vous faites la demande doit implémenter CORS pour autoriser JavaScript à partir de votre site Web. Votre JavaScript ne peut pas s'autoriser à accéder à un autre site Web.

Quentin
la source
1
Faites plutôt les demandes à Flickr depuis votre serveur.
Quentin
Je n'utilise pas de serveur mais je peux utiliser node.js. Pouvez-vous me montrer le chemin avec ça?
ankitr
1
Pas dans l'espace disponible dans un commentaire. C'est plutôt un grand ensemble de sujets.
Quentin
Pourquoi est-ce que je peux obtenir une réponse en https://www.google.comutilisant une application comme POSTMAN, mais lorsque j'essaie GETd' https://www.google.comutiliser un appel AJAX, j'obtiens l'erreur CORS? N'y a-t-il aucun moyen que je puisse faire en sorte que l'appel AJAX se comporte de la même manière que l'appel de POSTMAN?
AjaxLeung
6
@AlexLeung - Postman est une application installée. Si votre site Web pouvait demander à mon navigateur de demander des données à Google et de les lire, votre site Web pourrait demander ma page Boîte de réception Gmail et lire tous mes e-mails. Ce serait terrible.
Quentin
64

J'ai eu un problème similaire et pour moi, cela se résumait à l'ajout des en-têtes HTTP suivants à la réponse du destinataire :

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

Vous préférerez peut-être ne pas utiliser le *à la fin, mais uniquement le nom de domaine de l'hôte qui envoie les données. Comme*.example.com

Mais cela n'est possible que lorsque vous avez accès à la configuration du serveur.

Erwin
la source
2
Je suis nouveau dans AngularJs. Pouvez-vous me dire où mettre en œuvre cela?
ankitr
18
@Ankit Vous devez ajouter ces en-têtes dans le serveur , pas dans AngularJS.
Felipe
2
@techcraver - Vous n'avez pas besoin d'un accès opérationnel à la configuration du serveur - transmettez simplement les en-têtes depuis votre script. Si vous avez un backend PHP, ce seraitheader('Access-Control-Allow-Origin: *');
davidkonrad
@davidkonrad: J'ai créé toute l'application en angular v4. Pouvez-vous dire où je dois inclure ces en-têtes: /
Pygirl
@Pygirl, je crois que vous avez rendu le côté client anguleux? Vous devez ajouter des en-têtes au côté serveur de réponse, comment et où dépendent de la technologie. S'il s'agit d'un script PHP que vous appelez à partir d'un angulaire Http.get()ou similaire, ajoutez header('Access-Control-Allow-Origin: *')comme toute première sortie dans le script PHP appelé (c'est-à-dire avant de sortir la réponse réelle, JSON, quoi que ce soit.
davidkonrad
9

Essayez d'utiliser le service de ressources pour consommer 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' });
});

Modèle:

<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>
Ali Habibzadeh
la source
4

Ce problème se produit en raison de la stratégie de modèle de sécurité d'application Web qui est la même stratégie d'origine. Dans le cadre de la stratégie, un navigateur Web permet aux scripts contenus dans une première page Web d'accéder aux données d'une deuxième page Web, mais uniquement si les deux pages Web ont la même origine. Cela signifie que le demandeur doit correspondre à l'hôte, au protocole et au port exacts du site demandeur.

Nous avons plusieurs options pour résoudre ce problème d'en-tête CORS.

  1. Utilisation du proxy - Dans cette solution, nous exécuterons un proxy de telle sorte que lorsque la demande passe par le proxy, il apparaîtra comme s'il s'agissait de la même origine. Si vous utilisez le nodeJS, vous pouvez utiliser cors-n'importe où pour faire le proxy. https://www.npmjs.com/package/cors-anywhere .

    Exemple : -

    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 est une méthode pour envoyer des données JSON sans se soucier des problèmes inter-domaines.Il n'utilise pas l'objet XMLHttpRequest, mais utilise la <script>balise à la place. https://www.w3schools.com/js/js_json_jsonp.asp

  3. Côté serveur - Du côté serveur, nous devons activer les demandes d'origine croisée. Nous allons d'abord obtenir les demandes de contrôle en amont (OPTIONS) et nous devons autoriser la demande qui est le code d'état 200 (ok).

    Les requêtes contrôlées en amont envoient d'abord un en-tête de requête HTTP OPTIONS à la ressource de l'autre domaine, afin de déterminer si la requête réelle peut être envoyée en toute sécurité. Les demandes intersites sont contrôlées en amont comme ceci, car elles peuvent avoir des implications sur les données des utilisateurs. En particulier, une requête est contrôlée en amont si elle utilise des méthodes autres que GET ou POST. De plus, si POST est utilisé pour envoyer des données de requête avec un Content-Type autre que application / x-www-form-urlencoded, multipart / form-data ou text / plain, par exemple si la requête POST envoie une charge XML au serveur en utilisant application / xml ou text / xml, la demande est alors contrôlée en amont. Il définit des en-têtes personnalisés dans la demande (par exemple, la demande utilise un en-tête tel que X-PINGOTHER)

    Si vous utilisez le ressort, le simple fait d'ajouter le code ci-dessous résoudra le problème. Ici, j'ai désactivé le jeton csrf qui n'a pas d'importance, activer / désactiver selon vos besoins.

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

    Si vous utilisez la sécurité à ressort, utilisez le code ci-dessous avec le code ci-dessus.

    @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();
        }
    
    }
Niru
la source
2

J'ai rencontré un problème similaire comme celui-ci, le problème était avec le backend. J'utilisais le serveur de nœuds (Express). J'ai eu une demande d'obtention du frontend (angulaire) comme indiqué ci-dessous

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

Mais cela a donné l'erreur suivanteL'erreur

Ceci est le code backend écrit en utilisant express sans les en-têtes

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

Après avoir ajouté un en-tête à la méthode, le problème a été résolu

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

Vous pouvez obtenir plus de détails sur l' activation de CORS sur Node JS

Janith Udara
la source
1

Répondu par moi-même.

CORS angular js + restEasy on POST

Enfin, je suis arrivé à cette solution de contournement: la raison pour laquelle cela a fonctionné avec IE est parce que IE envoie directement un POST au lieu de commencer par une demande de contrôle en amont pour demander la permission. Mais je ne sais toujours pas pourquoi le filtre n'a pas pu gérer une requête OPTIONS et envoie par défaut des en-têtes qui ne sont pas décrits dans le filtre (cela semble être un remplacement pour ce seul cas ... peut-être une chose restEasy ... .)

J'ai donc créé un chemin OPTIONS dans mon service de repos qui réécrit la réponse et inclut les en-têtes dans la réponse à l'aide de l'en-tête de réponse

Je cherche toujours la manière propre de le faire si quelqu'un a déjà été confronté à cela.

J.Doe
la source
1

Apache / HTTPD a tendance à être présent dans la plupart des entreprises ou si vous utilisez Centos / etc à la maison. Donc, si vous en avez, vous pouvez créer un proxy très facilement pour ajouter les en-têtes CORS nécessaires.

J'ai un billet de blog sur ce ici que je souffrais avec lui à quelques reprises récemment. Mais le plus important est simplement d'ajouter ceci à votre fichier /etc/httpd/conf/httpd.conf et de vous assurer que vous faites déjà "Listen 80":

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

Cela garantit que toutes les requêtes adressées aux URL sous your-server-ip: 80 / SomePath acheminent vers http: // target-ip: 8080 / SomePath (l'API sans prise en charge CORS ) et qu'elles retournent avec le bon Access-Control-Allow- En-tête Origin pour leur permettre de travailler avec votre application Web.

Bien sûr, vous pouvez modifier les ports et cibler l'ensemble du serveur plutôt que SomePath si vous le souhaitez.

John Humphreys - w00te
la source
1

Cette réponse décrit deux façons de contourner les API qui ne prennent pas en charge CORS:

  • Utilisez un proxy CORS
  • Utilisez JSONP si l'API le prend en charge

Une solution de contournement consiste à utiliser un 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>

Pour plus d'informations, consultez


Utilisez JSONP si l'API le prend en charge:

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

La DEMO sur PLNKR

Pour plus d'informations, consultez

georgeawg
la source
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);
shiva samalla
la source
"Et ajoutez également le code suivant dans votre fichier WebApiConfig" - La question est de faire une demande à Flickr. Leurs serveurs ne sont pas sous le contrôle du PO. Flickr n'utilise probablement pas ASP.NET non plus.
Quentin
0

nous pouvons activer CORS dans le frontend en utilisant le module ngResourse. Mais le plus important, nous devrions avoir ce morceau de code tout en faisant la requête ajax dans le contrôleur,

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

En outre, vous devez ajouter ngResourse CDN dans la partie de script et l'ajouter en tant que dépendance dans le module d'application.

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

Ensuite, utilisez "ngResourse" dans la section des dépendances du module d'application

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

Alok Ranjan
la source