[ionic] CORS(Cross Origin Resource Sharing)
在ionic
專案中,如果你使用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. |
Chrome 瀏覽器有外掛(套件)可以直接解決這個問題:
使用Allow-Control-Allow-Origin: *外掛:
設定-更多工具-擴充套件程式 找到Allow-Control-Allow-Origin: *外掛
那麼,什麼是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請求,您的主機源將由Origin標頭指定,該標頭會自動包含在瀏覽器的CORS請求中。因為ionicframework.com
和主機api.com
並不匹配,我們從ionicframework.com
的請求必須要經過伺服器的批准,才能夠訪問該資源,以Http Options請求頭的形式。
如果我們遇到了上面的錯誤,說明伺服器沒有給我們的地址ionicframework.com
賦予訪問該資源的許可權。
下面來看一下,當通過ionic serve
,ionic run
,ionic run -l
三種不同方式執行app時,你的origin
在瀏覽器中執行時
在執行ionic serve
時,發生了什麼?
- 啟動了一個本地web伺服器
- 你的瀏覽器自動打開了指向本地伺服器地址的一個頁面
這時你可以看到你的app載入到了你的瀏覽器上,地址是http://localhost:8100
(如果你選擇了localhost)。
這時,你的origin
是localhost:8100
。
任何傳送到主機而不是localhost:8100
的AJAX請求,都將以localhost:8100
作為其origin
,因此需要經過CORS預檢請求,來檢視是否有許可權訪問該資源。
在裝置上執行時
當執行ionic run
時,又發生了些什麼呢?
- 應用程式檔案被複制到裝置(或模擬器)。
- app開始執行,然後啟動一個裝置(或模擬器)上的瀏覽器來開啟這些檔案,類似
file://some/path/www/index.html
這樣,你的origin
將不存在了,因為你正在運行於一個檔案(file://
)URI。理所當然,你的任何向外請求將不需要通過CORS預檢。
在裝置上通過自動載入(livereload)執行時
執行ionic run -l
時,又將發生什麼呢?
- 首先啟動一個本地web伺服器
- 裝置(或模擬器)上app開始執行,啟動一個瀏覽器來開地址
http://192.168.1.1:8100
(你的本地IP地址)上的檔案。
這時,你的origin
是192.168.1.1:8100
。
任何傳送到主機而不是192.168.1.1:8100
的AJAX請求,都將以192.168.1.1:8100
作為其origin
,因此需要經過CORS預檢請求,來檢視是否有許可權訪問該資源。
在Ionic中處理CORS問題
CORS問題僅僅發生在,當我們以ionic serve
或ionic run -l
來執行或測試app的時候。
有2種方法來解決這個問題.
第一種,比較容易的方法,是設定遠端伺服器,讓它可以接受所有源的請求。第二種,有時候我們沒有許可權去修改遠端伺服器,此時就需要一個不指定源的請求。
我們可以使用代理伺服器來實現這個方案。ionic cli
給我們提供了一個很方便的配置代理伺服器的方法,下面我們看一下如何來實現。
Ionic CLI代理伺服器
下面是關於代理(proxy)的一個定義:
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.
在計算機網路中,代理伺服器是一個客戶端請求的中介伺服器(計算機系統或應用),它用於幫助客戶端尋找位於其他伺服器上的資源。
要解決我們在ionic中遇到的CORS問題,我們需要設定一個代理伺服器,它接受我們的請求,並向API端點發出新的請求,然後接受響應,並將其轉發回我們的應用程式。
由於代理伺服器需要向目標傳送新的請求,因此請求中將不會有origin
(源),也就不再需要CORS了。 需要非常注意一點是,origin
是瀏覽器自動幫我們新增在請求頭中的。
Ionic CLI具有為客戶端請求設定代理伺服器的能力,用來幫助您解決CORS問題,下面來看一下Ionic CLI設定代理的方法。
設定代理伺服器
再次說明一下,僅僅在ionic serve
或ionic run -l
的時候需要設定代理。
首先,我們需要在ionic.project
(注意,ionic2 cli新版本已經將此檔案重新命名為ionic.config.json
)配置檔案中設定代理。 這將告訴Ionic本地伺服器監聽這些路徑並將這些請求轉發到目標網址。
在app中,我們需要替換包含endpoint的URLS,並設定為我們的代理伺服器地址。(In our app, we will need to replace our endpoint URLS to be set to the proxy server address for when we are running serve or run -l.)
什麼是
endpoint
?在專案中 endpoint有時也寫作baseUrl,比如這個
http://cors.api.com/api/book/2/detail
的endpoint是http://cors.api.com/api
,再比如http://localhost:8101/api/user/123
的endpoint是http://localhost:8101/api
。筆者沒有想到好的中文來替代這個英文單詞,所以沒有進行翻譯,希望小夥伴們能夠理解它的意思就好啦
~
我們可以通過設定一些gulp任務,用gulp中的替換模組(replace)來替換出這些URLS,來簡化這一步驟。
比較推薦的方法是設定一個Angular Constant來同一設定的endpoint,方便統一進行修改和維護。
下面是操作步驟。我們需要設定一個Angular Service ApiEndpoint來獲取資料。
配置代理地址
配置代理有兩個需要注意的地方。
path
:你在本地Ionic伺服器上訪問它們的路徑proxyUrl
:你最終希望通過API呼叫達到的proxyUrl
修改ionic.config.json
(舊版本Ionic CLI是ionic.project
):
{ "name": "proxy-example", "app_id": "", "proxies": [ { "path": "/api", "proxyUrl": "http://cors.api.com/api" } ] } |
如上所述,當您請求訪問http://localhost:8100/api
的ionic伺服器時,它會替你代理請求到http://cors.api.com/api
上。這樣,就不需要CORS了。
設定Angular Constant
很容易將你的ApiEndpoint設定為Angular Constant。下面,我們已經將ApiEndpoint指定為我們的代理url。然後,我們就可以使用這個url作為一個常數。
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' // }) |
設定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實現URL自動切換
首先,需要安裝replace module
npm install --save replace
|
然後,我們需要修改gulpfile.js
,並新增2個任務,來新增代理地址、移出代理地址。
// `npm install --save replace` var replace = require('replace'); //注意下面的檔案地址,它是包含你endpoint或baseurl的檔案 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 serve
或ionic run -l
的時候,一個處理CORS問題的方法。
我們知道,如果按這個方法處理,當我們需要在ionic serve
和ionic run -l
切換的時候,將ApiEndpoint替換出來可能會比較麻煩。因此我們推薦使用gulp在程式啟動時自動完成這個過程。
其實,最簡單的處理CORS問題的方法,是要求你的api提供伺服器來允許所有的origin
。但是,有時候我們無法這麼做。
使用Angular Constant和replace module會給我們一個愉快的解決方法來處理CORS問題。
如果你需要一個具體的例子,可以看一下這個示例工程。
上面說的就是所有的當你訪問一個API伺服器時,關於處理CORS問題的全部。