Cómo habilitar CORS en AngularJs

147

He creado una demostración usando JavaScript para la API de búsqueda de fotos de Flickr. Ahora lo estoy convirtiendo a AngularJs. He buscado en internet y encontré la siguiente configuración.

Configuración:

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

Servicio:

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

Controlador:

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

Pero aún así recibí el mismo error. Aquí hay algunos enlaces que probé:

XMLHttpRequest no puede cargar la URL. Origen no permitido por Access-Control-Allow-Origin

http://goo.gl/JuS5B1

ankitr
fuente
1
Debe solicitar los datos de su proxy, todavía lo solicita directamente desde flickr.
Quentin
@quentin Gracias por su rápida respuesta. ¿Me puede dar una demostración?
ankitr
1
Simplemente cambie la URL de flickr.com a la URL de su proxy
Quentin
1
¿Pero cómo voy a llamar a Flickr? ya que necesito las imágenes de flickr
ankitr
2
El cliente llama al proxy. El proxy llama a flickr. Eso es lo que significa proxy. (Su código de proxy ... no es un proxy, es un servidor web para servir JSON y JSONP desde archivos estáticos).
Quentin

Respuestas:

193

Usted no El servidor al que realiza la solicitud debe implementar CORS para otorgar JavaScript desde el acceso a su sitio web. Su JavaScript no puede otorgarse permiso para acceder a otro sitio web.

Quentin
fuente
1
En su lugar, haga las solicitudes a Flickr desde su servidor.
Quentin
No estoy usando un servidor pero puedo usar node.js. ¿Me puede mostrar el camino con esto?
ankitr
1
No en el espacio disponible en un comentario. Es más bien un gran conjunto de temas.
Quentin
¿Por qué puedo obtener una respuesta al https://www.google.comusar una aplicación como POSTMAN, pero cuando trato GETde https://www.google.comusar una llamada AJAX obtengo el error CORS? ¿No hay forma de que la llamada AJAX se comporte de manera similar a la llamada de POSTMAN?
AjaxLeung
55
@AlexLeung - Postman es una aplicación instalada. Si su sitio web puede hacer que mi navegador solicite datos de Google y leerlos, entonces su sitio web podría solicitar mi página de bandeja de entrada de GMail y leer todo mi correo electrónico. Eso sería terrible
Quentin
64

Tuve un problema similar y para mí se redujo a agregar los siguientes encabezados HTTP en la respuesta del extremo receptor :

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

Es posible que prefiera no usar *al final, sino solo el nombre de dominio del host que envía los datos. Me gusta*.example.com

Pero esto solo es factible cuando tiene acceso a la configuración del servidor.

Erwin
fuente
2
Soy nuevo en AngularJs. ¿Me puede decir dónde implementar esto?
Ankitr
17
@Ankit Debe agregar estos encabezados en el servidor , no en AngularJS.
Felipe
2
@techcraver: no necesita acceso operativo a la configuración del servidor, simplemente pase los encabezados desde su script. Si tiene un backend PHP, seríaheader('Access-Control-Allow-Origin: *');
davidkonrad
@davidkonrad: he creado toda la aplicación en angular v4. ¿Puedes decir dónde tengo que incluir estos encabezados: /
Pygirl
@Pygirl, ¿creo que has hecho que el lado del cliente sea angular? Debe agregar encabezados al lado del servidor de respuesta, cómo y dónde dependen de la tecnología. Si es un script PHP al que llama desde un angular Http.get()o similar, agregue header('Access-Control-Allow-Origin: *')como la primera salida en el script PHP llamado (es decir, antes de generar la respuesta real, JSON, lo que sea.)
davidkonrad
9

Intente usar el servicio de recursos para consumir 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' });
});

Modelo:

<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
fuente
4

Este problema ocurre debido a la política del modelo de seguridad de la aplicación web que es la Política del mismo origen Según la política, un navegador web permite que los scripts contenidos en una primera página web accedan a los datos de una segunda página web, pero solo si ambas páginas tienen el mismo origen. Eso significa que el solicitante debe coincidir con el host, el protocolo y el puerto exactos del sitio solicitante.

Tenemos múltiples opciones para superar este problema de encabezado CORS.

  1. Uso de proxy : en esta solución, ejecutaremos un proxy de modo que cuando la solicitud pase por el proxy, parezca que tiene el mismo origen. Si está utilizando el nodeJS , puede usar cors-anywhere para hacer el proxy. https://www.npmjs.com/package/cors-anywhere .

    Ejemplo :

    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 es un método para enviar datos JSON sin preocuparse por problemas entre dominios. No utiliza el objeto XMLHttpRequest. En su lugar, utiliza la <script>etiqueta. https://www.w3schools.com/js/js_json_jsonp.asp

  3. Lado del servidor: en el lado del servidor, debemos habilitar las solicitudes de origen cruzado. Primero, recibiremos las solicitudes Preflight (OPTIONS) y debemos permitir la solicitud que es el código de estado 200 (ok).

    Las solicitudes verificadas previamente envían primero un encabezado de solicitud de OPCIONES HTTP al recurso en el otro dominio, para determinar si la solicitud real es segura de enviar. Las solicitudes de sitios cruzados se verifican de esta manera, ya que pueden tener implicaciones para los datos del usuario. En particular, una solicitud se verifica previamente si utiliza métodos distintos a GET o POST. Además, si POST se utiliza para enviar datos de solicitud con un tipo de contenido que no sea application / x-www-form-urlencoded, multipart / form-data o text / plain, por ejemplo, si la solicitud POST envía una carga XML al servidor usando application / xml o text / xml, la solicitud se verifica previamente. Establece encabezados personalizados en la solicitud (por ejemplo, la solicitud utiliza un encabezado como X-PINGOTHER)

    Si está utilizando el resorte, solo agregar el siguiente código resolverá el problema. Aquí he deshabilitado el token csrf que no importa habilitar / deshabilitar de acuerdo a sus requerimientos.

    @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 está utilizando la seguridad de primavera, use el código siguiente junto con el código anterior.

    @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
fuente
2

Encontré un problema similar como este, el problema fue con el backend. Estaba usando el servidor de nodo (Express). Recibí una solicitud de la interfaz (angular) como se muestra a continuación

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

Pero dio el siguiente errorEl error

Este es el código de backend escrito usando express sin los encabezados

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

Después de agregar un encabezado al método se resolvió el problema

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

Puede obtener más detalles sobre cómo habilitar CORS en el nodo JS

Janith Udara
fuente
1

Respondido por mí mismo.

CORS angular js + restEasy en POST

Bueno, finalmente llegué a esta solución: la razón por la que funcionó con IE es porque IE envía directamente una POST en lugar de primero una solicitud de verificación previa para pedir permiso. Pero todavía no sé por qué el filtro no pudo administrar una solicitud de OPCIONES y envía encabezados predeterminados que no se describen en el filtro (parece una anulación para ese único caso ... tal vez una cosa fácil de descansar ... .)

Así que creé una ruta OPTIONS en mi servicio de descanso que reescribe la respuesta e incluye los encabezados en la respuesta usando el encabezado de respuesta

Todavía estoy buscando la manera limpia de hacerlo si alguien se enfrentó a esto antes.

J.Doe
fuente
1

Apache / HTTPD tiende a estar presente en la mayoría de las empresas o si está usando Centos / etc en casa. Entonces, si tiene eso, puede hacer un proxy muy fácilmente para agregar los encabezados CORS necesarios.

Tengo una publicación de blog sobre esto aquí, ya que sufrí con ella varias veces recientemente. Pero lo importante es simplemente agregar esto a su archivo /etc/httpd/conf/httpd.conf y asegurarse de que ya está haciendo "Listen 80":

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

Esto garantiza que todas las solicitudes a las URL en la ruta your-server-ip: 80 / SomePath a http: // target-ip: 8080 / SomePath (la API sin compatibilidad con CORS) y que regresen con la correcta Access-Control-Allow- Encabezado de origen para permitirles trabajar con su aplicación web.

Por supuesto, puede cambiar los puertos y apuntar a todo el servidor en lugar de SomePath si lo desea.

John Humphreys - w00te
fuente
1

Esta respuesta describe dos formas de solucionar las API que no admiten CORS:

  • Use un proxy CORS
  • Use JSONP si la API lo admite

Una solución alternativa es usar 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>

Para más información, ver


Use JSONP si la API lo admite:

 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 en PLNKR

Para más información, ver

georgeawg
fuente
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
fuente
"Y también agregue el siguiente código en su archivo WebApiConfig" - La pregunta es sobre hacer una solicitud a Flickr. Sus servidores no están bajo el control del OP. Flickr probablemente tampoco esté usando ASP.NET.
Quentin
0

podemos habilitar CORS en la interfaz utilizando el módulo ngResourse. Pero lo más importante, deberíamos tener este código mientras hacemos la solicitud ajax en el controlador,

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

Además, debe agregar ngResourse CDN en la parte del script y agregarlo como una dependencia en el módulo de la aplicación.

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

Luego use "ngResourse" en la sección de dependencia del módulo de la aplicación

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

Alok Ranjan
fuente