วิธีเปิดใช้งาน CORS ใน AngularJs


147

ฉันสร้างตัวอย่างโดยใช้ JavaScript สำหรับ API การค้นหาภาพถ่าย Flickr ตอนนี้ฉันกำลังแปลงเป็น 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
Quentin

@quentin ขอบคุณสำหรับการตอบกลับอย่างรวดเร็ว คุณกรุณาช่วยสาธิตให้ฉันได้ไหม
ankitr

1
คุณเพียงแค่เปลี่ยน URL จาก flickr.com เป็น URL ของพร็อกซีของคุณ
Quentin

1
แต่ฉันจะเรียก Flickr ได้อย่างไร? ตามที่ฉันต้องการภาพจาก Flickr
ankitr

2
ไคลเอนต์เรียกพร็อกซี พร็อกซีเรียก flickr นั่นคือความหมายของพร็อกซี่ (รหัสพร็อกซีของคุณ…ไม่ใช่พร็อกซีเป็นเว็บเซิร์ฟเวอร์สำหรับการให้บริการ JSON และ JSONP จากไฟล์คงที่)
Quentin

คำตอบ:


193

คุณทำไม่ได้ เซิร์ฟเวอร์ที่คุณกำลังร้องขอต้องใช้ CORS เพื่อให้สิทธิ์ JavaScript จากการเข้าถึงเว็บไซต์ของคุณ JavaScript ของคุณไม่อนุญาตให้ตัวเองเข้าถึงเว็บไซต์อื่น


1
ทำการร้องขอไปยัง Flickr จากเซิร์ฟเวอร์ของคุณแทน
Quentin

ฉันไม่ได้ใช้เซิร์ฟเวอร์ แต่ฉันสามารถใช้ node.js คุณสามารถแสดงให้ฉันเห็นทางนี้ได้ไหม?
ankitr

1
ไม่ได้อยู่ในพื้นที่ว่างในความคิดเห็น มันค่อนข้างเป็นชุดใหญ่ของหัวข้อ
Quentin

ทำไมมันจึงเป็นที่ฉันสามารถรับการตอบสนองจากhttps://www.google.comการใช้โปรแกรมเช่น Postman แต่เมื่อฉันพยายามGETจากhttps://www.google.comการใช้โทร AJAX ฉันได้รับข้อผิดพลาดล ธ ? ไม่มีวิธีที่ฉันสามารถโทร AJAX ทำงานคล้ายกับการโทรจาก POSTMAN ได้หรือไม่?
AjaxLeung

5
@AlexLeung - บุรุษไปรษณีย์เป็นแอปพลิเคชั่นที่ติดตั้ง หากเว็บไซต์ของคุณสามารถทำให้เบราว์เซอร์ของฉันร้องขอข้อมูลจาก Google และอ่านได้เว็บไซต์ของคุณสามารถร้องขอหน้า GMail Inbox ของฉันและอ่านอีเมลทั้งหมดของฉัน นั่นคงจะแย่มาก
Quentin

64

ฉันมีปัญหาที่คล้ายกันและสำหรับฉันมันต้มลงเพื่อเพิ่มส่วนหัว HTTP ต่อไปนี้ที่การตอบสนองของการรับปลาย :

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

คุณอาจไม่ต้องการใช้*ที่ส่วนท้าย แต่มีเพียงชื่อโดเมนของโฮสต์ที่ส่งข้อมูล ชอบ*.example.com

แต่นี่เป็นไปได้ก็ต่อเมื่อคุณสามารถเข้าถึงการกำหนดค่าของเซิร์ฟเวอร์


2
ฉันใหม่ใน AngularJs ได้โปรดบอกฉันว่าจะใช้สิ่งนี้ได้ที่ไหน?
ankitr

17
@Ankit คุณต้องเพิ่มส่วนหัวเหล่านี้ในเซิร์ฟเวอร์ไม่ใช่ใน AngularJS
เฟลิเป้

2
@techcraver - คุณไม่จำเป็นต้องเข้าถึงการกำหนดค่าเซิร์ฟเวอร์ - เพียงแค่ผ่านส่วนหัวจากภายในสคริปต์ของคุณ หากคุณมีแบ็กเอนด์ PHP มันจะเป็นเช่นนั้นheader('Access-Control-Allow-Origin: *');
davidkonrad

@davidkonrad: ฉันได้สร้างแอพทั้งหมดใน angular v4 แล้ว คุณช่วยบอกได้ไหมว่าฉันต้องใส่หัวข้อ
ไหนไว้

@ ไพค์เกิร์ล, ฉันเชื่อว่าคุณทำไคลเอนต์ในมุม? คุณต้องเพิ่มส่วนหัวในเซิร์ฟเวอร์การตอบสนองอย่างไรและที่ไหนขึ้นอยู่กับเทคโนโลยี ถ้ามันเป็นสคริปต์ PHP ที่คุณโทรมาจากเชิงมุมHttp.get()หรือคล้ายกันให้เพิ่ม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

ปัญหานี้เกิดขึ้นเนื่องจากนโยบายโมเดลความปลอดภัยของเว็บแอ็พพลิเคชันที่เป็นนโยบาย Origin ดั้งเดิมภายใต้นโยบายเว็บเบราว์เซอร์อนุญาตให้สคริปต์ที่มีอยู่ในเว็บเพจแรกในการเข้าถึงข้อมูลในหน้าเว็บที่สอง แต่เฉพาะในกรณีที่ทั้งสองหน้าเว็บ นั่นหมายความว่าผู้ร้องขอต้องตรงกับโฮสต์โปรโตคอลและพอร์ตของไซต์ที่ขอ

เรามีตัวเลือกมากมายในการแก้ไขปัญหาส่วนหัว 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. ฝั่งเซิร์ฟเวอร์ - ฝั่งเซิร์ฟเวอร์เราจำเป็นต้องเปิดใช้งานการร้องขอข้ามแหล่ง ก่อนอื่นเราจะได้รับคำขอ preflighted (OPTIONS) และเราต้องอนุญาตคำขอที่เป็นรหัสสถานะ200 (ok)

    การร้องขอแบบ preflighted ก่อนจะส่งส่วนหัวคำขอ HTTP OPTIONS ไปยังทรัพยากรในโดเมนอื่นเพื่อตรวจสอบว่าคำขอจริงนั้นปลอดภัยที่จะส่งหรือไม่ คำขอข้ามไซต์จะทำการ preflighted เช่นนี้เนื่องจากอาจมีผลกระทบต่อข้อมูลผู้ใช้ โดยเฉพาะอย่างยิ่งคำขอจะถูก preflighted ถ้ามันใช้วิธีการอื่นที่ไม่ใช่ GET หรือ POST นอกจากนี้หาก POST ใช้ในการส่งข้อมูลคำขอด้วย Content-Type นอกเหนือจาก application / x-www-form-urlencoded, multipart / form-data หรือ text / plain เช่นถ้าคำขอ POST ส่ง payload XML ไปยังเซิร์ฟเวอร์ ใช้ application / xml หรือ text / xml ดังนั้นคำขอจะถูก preflighted มันตั้งค่าส่วนหัวที่กำหนดเองในคำขอ (เช่นคำขอใช้ส่วนหัวเช่น 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("*");
                }
            };
        }
    }

    หากคุณใช้การรักษาความปลอดภัยสปริงให้ใช้โค้ดด้านล่างพร้อมกับรหัสด้านบน

    @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

ฉันพบปัญหาที่คล้ายกันเช่นนี้ปัญหาเกิดจากแบ็กเอนด์ ฉันใช้โหนดเซิร์ฟเวอร์ (Express) ฉันได้รับคำขอจากส่วนหน้า (มุม) ที่แสดงด้านล่าง

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

คุณสามารถรับรายละเอียดเพิ่มเติมเกี่ยวกับการเปิดใช้งาน CORS บน Node JS


1

ตอบเอง

CORS เชิงมุม js + วางได้ง่ายบน POST

ในที่สุดฉันก็มาถึงการแก้ปัญหานี้: เหตุผลที่ทำงานกับ IE ก็เพราะว่า IE ส่ง POST โดยตรงแทนที่จะเป็นคำขอ preflight แรกเพื่อขออนุญาต แต่ฉันก็ยังไม่รู้ว่าทำไมตัวกรองไม่สามารถจัดการคำขอ OPTIONS และส่งโดยส่วนหัวเริ่มต้นที่ไม่ได้อธิบายไว้ในตัวกรองได้ (ดูเหมือนจะเป็นการแทนที่สำหรับกรณีนั้น ... อาจเป็นเรื่องง่าย .)

ดังนั้นฉันจึงสร้างเส้นทางตัวเลือกในบริการส่วนที่เหลือของฉันที่เขียน reponse และรวมส่วนหัวในการตอบกลับโดยใช้ส่วนหัวการตอบกลับ

ฉันยังคงมองหาวิธีที่สะอาดที่จะทำถ้าใครเคยเจอเรื่องนี้มาก่อน


1

Apache / HTTPD มีแนวโน้มที่จะอยู่ในองค์กรส่วนใหญ่หรือถ้าคุณใช้ Centos / etc ที่บ้าน ดังนั้นถ้าคุณมีสิ่งนั้นคุณสามารถทำ proxy ได้อย่างง่ายดายเพื่อเพิ่มส่วนหัว 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>

สิ่งนี้ทำให้มั่นใจได้ว่าคำขอทั้งหมดไปยัง URL ภายใต้เส้นทางของคุณ - เซิร์ฟเวอร์ -ip: 80 / เส้นทาง SomePath ไปที่http: // target-ip: 8080 / SomePath (API ที่ไม่มีการสนับสนุน CORS) และพวกเขากลับมาพร้อมกับ Access-Control-Allow- ที่ถูกต้อง ส่วนหัวของ Origin เพื่อให้พวกเขาสามารถทำงานกับเว็บแอปของคุณได้

แน่นอนคุณสามารถเปลี่ยนพอร์ตและกำหนดเป้าหมายเซิร์ฟเวอร์ทั้งหมดแทนที่จะเป็น SomePath หากคุณต้องการ


1

คำตอบนี้แสดงสองวิธีในการแก้ปัญหา API ที่ไม่รองรับ CORS:

  • ใช้ CORS Proxy
  • ใช้ JSONP ถ้า API รองรับ

วิธีแก้ปัญหาหนึ่งคือใช้ 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>

สำหรับข้อมูลเพิ่มเติมโปรดดู


ใช้ JSONP หาก API รองรับ:

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

DEMO บน 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 เช่นกัน
Quentin

0

เราสามารถเปิดใช้งาน CORS ในส่วนหน้าโดยใช้โมดูล ngResourse แต่ที่สำคัญที่สุดเราควรมีรหัสนี้ในขณะที่ทำการร้องขอ 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']);

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.