1. 程式人生 > >[ionic] CORS(Cross Origin Resource Sharing)

[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)。

這時,你的originlocalhost: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地址)上的檔案。

這時,你的origin192.168.1.1:8100

任何傳送到主機而不是192.168.1.1:8100的AJAX請求,都將以192.168.1.1:8100作為其origin,因此需要經過CORS預檢請求,來檢視是否有許可權訪問該資源。

在Ionic中處理CORS問題

CORS問題僅僅發生在,當我們以ionic serveionic 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 serveionic 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 serveionic run -l的時候,一個處理CORS問題的方法。

我們知道,如果按這個方法處理,當我們需要在ionic serveionic run -l切換的時候,將ApiEndpoint替換出來可能會比較麻煩。因此我們推薦使用gulp在程式啟動時自動完成這個過程。

其實,最簡單的處理CORS問題的方法,是要求你的api提供伺服器來允許所有的origin。但是,有時候我們無法這麼做。

使用Angular Constant和replace module會給我們一個愉快的解決方法來處理CORS問題。

如果你需要一個具體的例子,可以看一下這個示例工程

上面說的就是所有的當你訪問一個API伺服器時,關於處理CORS問題的全部。