在ionic項目中,如果你使用ionic serve或者ionic run,并且開啟了動態(tài)加載(live reload),且訪問了遠端服務(wù)器的API,那么你就可能會遇到 跨域資源共享(CORS) 問題。出現(xiàn)類似下面的錯誤提示:
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? 為什么在這里會發(fā)生這個錯誤呢?
什么是CORS?
CORS = Cross origin resource sharing 跨域資源共享
origin是你當前所在頁面的主機地址。
如果你在瀏覽http://ionicframework.com/blog/handling-cors-issues-in-ionic頁面,它的origin就是ionicframework.com。在此頁面上,如果你要向http://cors.api.com/api發(fā)送一個AJAX請求,您的主機源將由Origin標頭指定,該標頭會自動包含在瀏覽器的CORS請求中。因為ionicframework.com和主機api.com并不匹配,我們從ionicframework.com的請求必須要經(jīng)過服務(wù)器的批準,才能夠訪問該資源,以Http Options請求頭的形式。
如果我們遇到了上面的錯誤,說明服務(wù)器沒有給我們的地址ionicframework.com賦予訪問該資源的權(quán)限。
下面來看一下,當通過ionic serve,ionic run,ionic run -l三種不同方式運行app時,你的origin分別是什么?
在瀏覽器中運行時
在執(zhí)行ionic serve時,發(fā)生了什么?
- 啟動了一個本地web服務(wù)器
- 你的瀏覽器自動打開了指向本地服務(wù)器地址的一個頁面
這時你可以看到你的app加載到了你的瀏覽器上,地址是http://localhost:8100(如果你選擇了localhost)。
這時,你的origin是localhost:8100。
任何發(fā)送到主機而不是localhost:8100的AJAX請求,都將以localhost:8100作為其origin,因此需要經(jīng)過CORS預(yù)檢請求,來查看是否有權(quán)限訪問該資源。
在設(shè)備上運行時
當執(zhí)行ionic run時,又發(fā)生了些什么呢?
- 應(yīng)用程序文件被復(fù)制到設(shè)備(或模擬器)。
- app開始運行,然后啟動一個設(shè)備(或模擬器)上的瀏覽器來打開這些文件,類似
file://some/path/www/index.html
這樣,你的origin將不存在了,因為你正在運行于一個文件(file://)URI。理所當然,你的任何向外請求將不需要通過CORS預(yù)檢。
在設(shè)備上通過自動加載(livereload)運行時
執(zhí)行ionic run -l時,又將發(fā)生什么呢?
- 首先啟動一個本地web服務(wù)器
- 設(shè)備(或模擬器)上app開始運行,啟動一個瀏覽器來開地址
http://192.168.1.1:8100(你的本地IP地址)上的文件。
這時,你的origin是192.168.1.1:8100。
任何發(fā)送到主機而不是192.168.1.1:8100的AJAX請求,都將以192.168.1.1:8100作為其origin,因此需要經(jīng)過CORS預(yù)檢請求,來查看是否有權(quán)限訪問該資源。
在Ionic中處理CORS問題
CORS問題僅僅發(fā)生在,當我們以ionic serve或ionic run -l來運行或測試app的時候。
有2種方法來解決這個問題.
第一種,比較容易的方法,是設(shè)置遠端服務(wù)器,讓它可以接受所有源的請求。第二種,有時候我們沒有權(quán)限去修改遠端服務(wù)器,此時就需要一個不指定源的請求。
我們可以使用代理服務(wù)器來實現(xiàn)這個方案。ionic cli給我們提供了一個很方便的配置代理服務(wù)器的方法,下面我們看一下如何來實現(xiàn)。
Ionic CLI代理服務(wù)器
下面是關(guān)于代理(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.
在計算機網(wǎng)絡(luò)中,代理服務(wù)器是一個客戶端請求的中介服務(wù)器(計算機系統(tǒng)或應(yīng)用),它用于幫助客戶端尋找位于其他服務(wù)器上的資源。
要解決我們在ionic中遇到的CORS問題,我們需要設(shè)置一個代理服務(wù)器,它接受我們的請求,并向API端點發(fā)出新的請求,然后接受響應(yīng),并將其轉(zhuǎn)發(fā)回我們的應(yīng)用程序。
由于代理服務(wù)器需要向目標發(fā)送新的請求,因此請求中將不會有origin(源),也就不再需要CORS了。 需要非常注意一點是,origin是瀏覽器自動幫我們添加在請求頭中的。
Ionic CLI具有為客戶端請求設(shè)置代理服務(wù)器的能力,用來幫助您解決CORS問題,下面來看一下Ionic CLI設(shè)置代理的方法。
設(shè)置代理服務(wù)器
再次說明一下,僅僅在ionic serve或ionic run -l的時候需要設(shè)置代理。
首先,我們需要在ionic.project(注意,ionic2 cli新版本已經(jīng)將此文件重命名為ionic.config.json)配置文件中設(shè)置代理。 這將告訴Ionic本地服務(wù)器監(jiān)聽這些路徑并將這些請求轉(zhuǎn)發(fā)到目標網(wǎng)址。
在app中,我們需要替換包含endpoint的URLS,并設(shè)置為我們的代理服務(wù)器地址。(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。筆者沒有想到好的中文來替代這個英文單詞,所以沒有進行翻譯,希望小伙伴們能夠理解它的意思就好啦~~~~~
我們可以通過設(shè)置一些gulp任務(wù),用gulp中的替換模塊(replace)來替換出這些URLS,來簡化這一步驟。
比較推薦的方法是設(shè)置一個Angular Constant來同一設(shè)置的endpoint,方便統(tǒng)一進行修改和維護。
下面是操作步驟。我們需要設(shè)置一個Angular Service ApiEndpoint來獲取數(shù)據(jù)。
配置代理地址
配置代理有兩個需要注意的地方。
-
path:你在本地Ionic服務(wù)器上訪問它們的路徑 -
proxyUrl:你最終希望通過API調(diào)用達到的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服務(wù)器時,它會替你代理請求到http://cors.api.com/api上。這樣,就不需要CORS了。
設(shè)置Angular Constant
很容易將你的ApiEndpoint設(shè)置為Angular Constant。下面,我們已經(jīng)將ApiEndpoint指定為我們的代理url。然后,我們就可以使用這個url作為一個常數(shù)。
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'
// })
設(shè)置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實現(xiàn)URL自動切換(可選)
(這是可選的,如果你使用手動配置就不需要gulp自動配置了)
首先,需要安裝replace module
npm install --save replace
然后,我們需要修改gulpfile.js,并添加2個任務(wù),來添加代理地址、移出代理地址。
// `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,
});
})
結(jié)束語
這個教程展示給你了在運行ionic serve或ionic run -l的時候,一個處理CORS問題的方法。
我們知道,如果按這個方法處理,當我們需要在ionic serve和ionic run -l切換的時候,將ApiEndpoint替換出來可能會比較麻煩。因此我們推薦使用gulp在程序啟動時自動完成這個過程。
其實,最簡單的處理CORS問題的方法,是要求你的api提供服務(wù)器來允許所有的origin。但是,有時候我們無法這么做。
使用Angular Constant和replace module會給我們一個愉快的解決方法來處理CORS問題。
如果你需要一個具體的例子,可以看一下這個示例工程。
上面說的就是所有的當你訪問一個API服務(wù)器時,關(guān)于處理CORS問題的全部。
作者:ten5743
鏈接:http://www.itdecent.cn/p/2b955fd32a45
來源:簡書