155

I have created a demo using JavaScript for Flickr photo search API. Now I am converting it to the AngularJs. I have searched on internet and found below configuration.

Configuration:

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

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

Controller:

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

But still I got the same error. Here are some links I tried:

XMLHttpRequest cannot load URL. Origin not allowed by Access-Control-Allow-Origin

http://goo.gl/JuS5B1

jonrsharpe
  • 115,751
  • 26
  • 228
  • 437
ankitr
  • 5,992
  • 7
  • 47
  • 66
  • 1
    You have to request the data from your proxy, you're still requesting it directly from flickr. – Quentin May 29 '14 at 10:24
  • @quentin Thanks for quick reply. Can you please give me a demo. – ankitr May 29 '14 at 10:31
  • 1
    You just change the URL from flickr.com to the URL of your proxy – Quentin May 29 '14 at 10:32
  • 1
    But how I gonna call flickr? as I need the images from flickr. – ankitr May 29 '14 at 10:38
  • 3
    The client calls the proxy. The proxy calls flickr. That is what proxy means. (Your proxy code … isn't a proxy, it's a web server for serving JSON and JSONP from static files). – Quentin May 29 '14 at 10:39
  • The bottom edit would be better as an answer to your question, not an edit – Liam Dec 20 '16 at 13:43
  • Related: [XMLHttpRequest cannot load https://www.\[website\].com/](http://stackoverflow.com/questions/35553500/xmlhttprequest-cannot-load-https-www-website-com) – Liam Dec 20 '16 at 14:06

10 Answers10

204

You don't. The server you are making the request to has to implement CORS to grant JavaScript from your website access. Your JavaScript can't grant itself permission to access another website.

Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 1
    Make the requests to Flickr from your server instead. – Quentin May 23 '14 at 08:25
  • I am not using a server but I can use node.js. Can you show me the way with this? – ankitr May 23 '14 at 08:29
  • 1
    Not in the space available in a comment. It's rather a big set of topics. – Quentin May 23 '14 at 08:31
  • Why is it that I can get a response from `https://www.google.com` using an application like POSTMAN, but when I try to `GET` from `https://www.google.com` using an AJAX call I get the CORS error? Is there no way I can make the AJAX call behave similarly to the call from POSTMAN? – Ozymandias Jan 27 '16 at 17:14
  • 6
    @AlexLeung — Postman is an installed application. If your website could make my browser request data from Google and read it, then your website could request my GMail Inbox page and read all of my email. That would be terrible. – Quentin Jan 31 '16 at 18:39
  • + On a Spring Server, juste add @CrossOrigin(origins = "http://localhost:8080") to your controller – amdev Aug 01 '16 at 13:58
  • @amdev — The OP doesn't run Flickr, so it doesn't matter what kind of server it is, they can't alter it. – Quentin Aug 01 '16 at 14:30
  • You can use a light reverse proxy server just for that purpose. – João Rodrigues Jun 17 '17 at 19:51
68

I had a similar problem and for me it boiled down to adding the following HTTP headers at the response of the receiving end:

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

You may prefer not to use the * at the end, but only the domainname of the host sending the data. Like *.example.com

But this is only feasible when you have access to the configuration of the server.

Erwin
  • 1,484
  • 1
  • 18
  • 32
  • 2
    I am new in AngularJs. Please can you tell me where to implement this? – ankitr May 26 '14 at 05:39
  • 21
    @Ankit You need to add these headers in the **server**, not in AngularJS. – Felipe Aug 05 '14 at 18:51
  • 2
    @techcraver - You dont need operational access to the configuration of the server - just pass the headers from within your script. If you have a PHP backend it would be `header('Access-Control-Allow-Origin: *');` – davidkonrad Feb 27 '16 at 23:32
  • @davidkonrad : I have created the whole app in angular v4. Can you tell where I have to include these header :/ – Pygirl Apr 09 '20 at 13:51
  • @Pygirl, I believe you have made the clientside in angular? You need to add headers to the response serverside, how and where depend on the technology. If it is a PHP script you call from an angular `Http.get()` or similar, add `header('Access-Control-Allow-Origin: *')` as the very first output in the called PHP script (i.e before you output the actual response, JSON, what ever. – davidkonrad Apr 09 '20 at 23:52
  • I have Asp.Net MVC and AngularJS, Already Implemented on web.config. But didn't get responce – Abdulla Sirajudeen Jun 10 '21 at 16:46
9

Try using the resource service to consume 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' });
});

Template:

<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>
makeitmorehuman
  • 11,287
  • 3
  • 52
  • 76
6

This issue occurs because of web application security model policy that is Same Origin Policy Under the policy, a web browser permits scripts contained in a first web page to access data in a second web page, but only if both web pages have the same origin. That means requester must match the exact host, protocol, and port of requesting site.

We have multiple options to over come this CORS header issue.

  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();
        }
    
    }
    
georgeawg
  • 48,608
  • 13
  • 72
  • 95
Niru
  • 489
  • 6
  • 15
2

Answered by myself.

CORS angular js + restEasy on POST

Well finally I came to this workaround: The reason it worked with IE is because IE sends directly a POST instead of first a preflight request to ask for permission. But I still don't know why the filter wasn't able to manage an OPTIONS request and sends by default headers that aren't described in the filter (seems like an override for that only case ... maybe a restEasy thing ...)

So I created an OPTIONS path in my rest service that rewrites the reponse and includes the headers in the response using response header

I'm still looking for the clean way to do it if anybody faced this before.

Community
  • 1
  • 1
J.Doe
  • 31
  • 1
2

I encountered a similar problem like this, problem was with the backend . I was using node server(Express). I had a get request from the frontend(angular) as shown below

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

But it gave the following errorThe error

This is the backend code written using express without the headers

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

After adding a header to the method problem was solved

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

You can get more details about Enabling CORS on Node JS

Janith Udara
  • 625
  • 1
  • 7
  • 15
2

This answer outlines two ways to workaround APIs that don't support CORS:

  • Use a CORS Proxy
  • Use JSONP if the API Supports it

One workaround is to use a 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>

For more information, see


Use JSONP if the API supports it:

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

The DEMO on PLNKR

For more information, see

Community
  • 1
  • 1
georgeawg
  • 48,608
  • 13
  • 72
  • 95
1

Apache/HTTPD tends to be around in most enterprises or if you're using Centos/etc at home. So, if you have that around, you can do a proxy very easily to add the necessary CORS headers.

I have a blog post on this here as I suffered with it quite a few times recently. But the important bit is just adding this to your /etc/httpd/conf/httpd.conf file and ensuring you are already doing "Listen 80":

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

This ensures that all requests to URLs under your-server-ip:80/SomePath route to http://target-ip:8080/SomePath (the API without CORS support) and that they return with the correct Access-Control-Allow-Origin header to allow them to work with your web-app.

Of course you can change the ports and target the whole server rather than SomePath if you like.

John Humphreys
  • 37,047
  • 37
  • 155
  • 255
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);
  • "And also add following code in your WebApiConfig file" — The question is about making a request to Flickr. Their servers are not under the control of the OP. Flickr probably isn't using ASP.NET either. – Quentin Mar 23 '18 at 11:00
0

we can enable CORS in the frontend by using the ngResourse module. But most importantly, we should have this piece of code while making the ajax request in the controller,

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

Also, you must add ngResourse CDN in the script part and add as a dependency in the app module.

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

Then use "ngResourse" in the app module dependency section

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

Jeff
  • 170
  • 2
  • 5
  • 19
Alok Ranjan
  • 967
  • 10
  • 10