Ionic CROS 跨域問題

原文鏈接:http://ionichina.com/topic/54f051698cbbaa7a56a49f98

譯者注:本人翻譯功力有限,所以文中難免有翻譯不準確的地方,湊合看吧,牛逼的話你看英文版的去,完事兒歡迎回來指正交流(^_^)

如果你通過 ionic serve 或者 ionic run 命令使用或 live reload 或者訪問過外部 API 結點,那麼你肯定遇到過 CORS 問題,譬如下面這樣:

XMLHttpRequest cannot load http://api.ionic.com/endpoint.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:8100' is therefore not allowed access.

那麼問題來了,什麼是 CORS 呢?又是什麼導致了這個問題嘞?

什麼是 CORS?

CORS=Cross origin resource sharing(跨域資源共享)

origin 就是你現在正在看的主站,你現在訪問的是http://ionicframework.com/blog/handling-cors-issues-in-ionic,那麼origin 就是 ionicframework.com

如果說我們向 http://cors.api.com/api 發起一個 AJAX 請求,那麼 host origin 會由被瀏覽器自動列入CORS請求的 Orgin header 指定好了*(原文:Say we send an AJAX request tohttp://cors.api.com/api, your host origin will be specified by the Origin header that is automatically included for CORS requests by the browser. )* 由於ionicframework.com api.com 的主機並不匹配,所以在一個HTTP OPTIONS請求報頭的 form 中我們所有從 ionicframework.com 發起的訪問服務器資源的請求必修得到服務器的授權。

假如上面的請求出現錯誤(不被服務器允許),那麼我們是無法從服務器訪問到(api.com上的)資源的。

讓我們來看一下當你通過ionic serveionic runionic run -l來運行 app 的時候origin 會是什麼。

瀏覽器中的運行

當你運行 ionic serve 時發生了什麼呢?

  • 啓動了一個本地 web 服務器
  • 你的瀏覽器打開並定位到本地服務器地址

這讓你看着你的應用加載到你電腦上一個瀏覽器裏,地址是:http://localhost:8100(如果你選擇了 localhost的話)。

你的 origin 就是 localhost:8100

任何的發送到其他不是 localhost:8100 主機上的 AJAX 請求都會把localhost:8100作爲他的 origin,這就會導致必須要經過一個 CORS 預檢來看是否可以訪問(非本機的)服務器資源。

設備上的運行

當你運行 ionic run 時發生了什麼呢?

  • app 所有的文件被拷貝到設備(或者模擬器)上。
  • app 運行起來,觸發手機/模擬器上的瀏覽器訪問已經被拷貝上去的文件,比如: file://some/path/www/index.html

因爲你正在運行的 URI 是 file://,所以你的 origin 將不會存在,所以任何向外的請求都不再需要 CORS 請求。

在設備使用 livereload 運行

當你運行 ionic run -l時又發生了什麼呢?

  • 啓動了一個本地服務器
  • app 運行起來,觸發手機/模擬器上的一個瀏覽器通過http://192.168.1.1:8100來運行文件(你的 本地 ip 可能是其他的)。

你的 origin 就會是 192.168.1.1:8100

任何一個發送到不是192.168.1.1:8100的服務器上的 AJAX 請求都會需要進行 CORS 預檢請求來看是否可以訪問到該服務器上的資源。

在 ionic 中解決 CORS 問題

CORS 問題只有在我們通過 ionic serve 或者 ionic run -l 來運行或測試應用的時候纔會遇到。

解決這個問題有兩個辦法:第一個,也是比較簡單的一個,就是在你的 API 服務器端允許所有的 origin,然而我們並不能控制我們訪問的所有的結點。我們需要的是一個不指定origin的請求。

我們可以通過使用代理服務器來解決這個問題。我們來看看 Ionic CLI 是怎樣提供了一個易配置的代理服務器的。

Ionic CLI代理服務器

關於代理的快速定義:

在計算機網絡中,代理服務器就是一個服務器(計算機系統或者應用程序),是客戶端發起的請求從其他服務器尋求資源的中間橋樑。

原文:In computer networks, a proxy server is a server (a computer system or an application) that acts as an intermediary for requests from clients seeking resources from other servers.

我們爲了避開 CORS 問題需要做的就是有一個代理服務器,可以接收我們的請求,想 API 結點發出一個新的請求,接收結點響應,之後反饋給我們的應用,從而避開 CORS 問題。

Ionic CLI 就有給你提供一個代理服務器從而避開所有可能會遇到的 CORS 問題的能力。

由於代理服務器向你的目標主機發起了一個新的請求,所以就不會再有 origin,也就不再需要 CORS 了。要注意,在瀏覽器增加了 Origin header 是很重要的。

設置代理服務器

注意,這些設置只有通過ionic serveionic run -l 運行應用才需要

首先我們需要在 ionic.project文件中設置我們的代理,這會告訴我們的 Ionic 本地服務器監聽這些地址,然後發送這些請求到我們的目標地址上。

在我們的應用中,當運行 serve run -l 時候,我們需要把要訪問的結點地址替換成代理服務器的地址。

使用gulp任務的 replace 模塊來轉換出口地址會簡單一點。

建議的方法是設置一個 Angular Constant 來定位到我們試圖代理的 API。

這就是我們下面要採用的方法。我們會同時設置一個 Angular Service 來從 API結點 獲取數據。

設置代理路徑

比如說我們想要訪問 http://cors.api.com/api,但並不允許我們來自 localhost的 origin。

代理的設置包括兩件事兒:在你本地 Ionic 服務器需要訪問的 path,最終需要訪問API的 proxyUrl

在你的 ionic.project 中像這樣設置:

{
  "name": "proxy-example",
  "app_id": "",
  "proxies": [
    {
      "path": "/api",
      "proxyUrl": "http://cors.api.com/api"
    }
  ]
}

通過ionic serve啓動你的服務器。

像我們上面指定的這樣,當你訪問 Ionic 服務器地址 http://localhost:8100/api 的時候,它會以你的名義訪問 http://cors.api.com/api

這樣,就不需要 CORS 了。

設置 Angular Constant

把你的 API結點設置成 Angular Constants是非常簡單的一件事兒。

下面我們就來把API結點指定成爲我們的代理 URL。

之後(發佈時候)我們會把正式的地址作爲 constant。

angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.constant('ApiEndpoint', {
  url: 'http://localhost:8100/api'
})
// For the real endpoint, we'd use this
// .constant('ApiEndpoint', {
//  url: 'http://cors.api.com/api'
// })

設置好之後你就能像下面這樣在應用中引入ApiEndpoint依賴,隨意調用這個constant了。

設置Angular Service

angular.module('starter.services', [])

//NOTE: We are including the constant `ApiEndpoint` to be used here.
.factory('Api', function($http, ApiEndpoint) {
  console.log('ApiEndpoint', ApiEndpoint)

  var getApiData = function() {
    return $http.get(ApiEndpoint.url + '/tasks')
      .then(function(data) {
        console.log('Got some data: ', data);
        return data;
      });
  };

  return {
    getApiData: getApiData
  };
})

通過 Gulp 自動轉換地址

這個過程中,我們需要修改gulpfile.js來添加兩個任務:添加代理和移除代理。

首先安裝replace模塊 -npm install --save replace

// `npm install --save replace`
var replace = require('replace');
var replaceFiles = ['./www/js/app.js'];

gulp.task('add-proxy', function() {
  return replace({
    regex: "http://cors.api.com/api",
    replacement: "http://localhost:8100/api",
    paths: replaceFiles,
    recursive: false,
    silent: false,
  });
})

gulp.task('remove-proxy', function() {
  return replace({
    regex: "http://localhost:8100/api",
    replacement: "http://cors.api.com/api",
    paths: replaceFiles,
    recursive: false,
    silent: false,
  });
})

結語

本教程向你展示了一個解決通過ionic serveionic run -l命令運行應用時候遇到的 CORS 問題的方法。

我們知道在ionic serveionic run -l之間轉換 API 結點地址的時候可能會是個麻煩,比較建議的方法是啓動一個 gulp 進程。

解決 CORS 問題最簡單的方法是讓 API 提供者允許所有的 hosts,然後這事兒有點兒不太現實。

使用 Angular constant 和 replace 模塊可以給我們一個避開 CORS 的折中的辦法。

如果你想看看完整的例子,可以看看這個示例項目

這就是你需要訪問一個有 CORS 限制的 API 服務器時候需要了解的所有事兒了。

如果你還有什麼疑問、問題或者想法,請在下面評論,或者在 twitter github 上聯繫我們。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章