瀏覽器跨域方法與基於Fetch的Web請求最佳實踐

本文從屬於筆者的Web前端中DOM系列文章.javascript

同源策略與跨域

同源策略

可謂同源?URL由協議、域名、端口和路徑組成,若是兩個URL的協議、域名和端口相同,則表示他們同源。瀏覽器的同源策略,限制了來自不一樣源的"document"或腳本,對當前"document"讀取或設置某些屬性,即從一個域上加載的腳本不容許訪問另一個域的文檔屬性。好比一個惡意網站的頁面經過iframe嵌入了銀行的登陸頁面(兩者不一樣源),若是沒有同源限制,惡意網頁上的javascript腳本就能夠在用戶登陸銀行的時候獲取用戶名和密碼。所謂道高一尺魔高一丈,雖然瀏覽器以同源策略限制了咱們隨意請求資源,可是從這個策略出現開始就有不少各類各樣的Hacker技巧來。node

JSONP

JSONP是較爲經常使用的一種跨域方式,不受到瀏覽器兼容性的限制,可是由於它只能以GET動詞進行請求,這樣就破壞了標準的REST風格,比較醜陋。JSONP本質上是利用<script>標籤的跨域能力實現跨域數據的訪問,請求動態生成的JavaScript腳本同時帶一個callback函數名做爲參數。其中callback函數本地文檔的JavaScript函數,服務器端動態生成的腳本會產生數據,並在代碼中以產生的數據爲參數調用 callback函數。當這段腳本加載到本地文檔時,callback函數就被調用。webpack

(1)瀏覽器端構造請求地址git

function resolveJson(result) {
    console.log(result.name);
}
var jsonpScript= document.createElement("script");
jsonpScript.type = "text/javascript";
jsonpScript.src = "http://www.qiute.com?callbackName=resolveJson";
document.getElementsByTagName("head")[0].appendChild(jsonpScript);

標準的Script標籤的請求地址爲:請求資源的地址+獲取函數的字段名+回調函數名稱,這裏的獲取函數的字段名是須要和服務端提早約定好,譬如jQuery中默認的獲取函數名就是callback。而resolveJson是咱們默認註冊的回調函數,注意,該函數名須要全局惟一,該函數接收服務端返回的數據做爲參數,而函數內容就是對於該參數的處理。es6

(2)服務端構造返回值github

在接受到瀏覽器端 script 的請求以後,從url的query的callbackName獲取到回調函數的名字,例子中是resolveJsonweb

而後動態生成一段javascript片斷去給這個函數傳入參數執行這個函數。好比:

resolveJson({name: 'qiutc'});

(3)客戶端以腳本方式執行服務端返回值

服務端返回這個 script 以後,瀏覽器端獲取到 script 資源,而後會當即執行這個 javascript,也就是上面那個片斷。這樣就能根據以前寫好的回調函數處理這些數據了。

CORS:跨域資源共享

跨域資源共享,Cross-Origin Resource Sharing是由W3C提出的一個用於瀏覽器以XMLHttpRequest方式向其餘源的服務器發起請求的規範。不一樣於JSONP,CORS是以Ajax方式進行跨域請求,須要服務端與客戶端的同時支持。目前CORS在絕大部分現代瀏覽器中都是支持的:

CORS標準定義了一個規範的HTTP Headers來使得瀏覽器與服務端之間能夠進行協商來肯定某個資源是否能夠由其餘域的客戶端請求得到。儘管不少的驗證與鑑權是由服務端完成,可是本質上大部分的檢查和限制仍是應該由瀏覽器完成。通常來講CORS會分爲Simple Request,簡單請求與Preflight,須要預檢的請求兩大類。其基本的流程以下:

預檢請求

當瀏覽器的請求方式是HEAD、GET或者POST,而且HTTP的頭信息中不會超出如下字段:

  • Accept

  • Accept-Language

  • Content-Language

  • Last-Event-ID

  • Content-Type:只限於三個值application/x-www-form-urlencoded、multipart/form-data、text/plain

時,瀏覽器會將該請求定義爲簡單請求,不然就是預檢請求。預檢請求會在正式通訊以前,增長一次HTTP查詢請求。瀏覽器先詢問服務器,當前網頁所在的域名是否在服務器的許可名單之中,以及可使用哪些HTTP動詞和頭信息字段。只有獲得確定答覆,瀏覽器纔會發出正式的XMLHttpRequest請求,不然就報錯。預檢請求的發送請求:

OPTIONS /cors HTTP/1.1
Origin: http://api.qiutc.me
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.qiutc.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

「預檢」請求用的請求方法是OPTIONS,表示這個請求是用來詢問的。頭信息裏面,關鍵字段是Origin,表示請求來自哪一個源。
除了Origin字段,」預檢」請求的頭信息包括兩個特殊字段:

  • Access-Control-Request-Method:該字段是必須的,用來列出瀏覽器的CORS請求會用到哪些HTTP方法,上例是PUT。

  • Access-Control-Request-Headers:該字段是一個逗號分隔的字符串,指定瀏覽器CORS請求會額外發送的頭信息字段,上例是X-Custom-Header。

預檢請求的返回:

HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://api.qiutc.me
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
  • Access-Control-Allow-Methods:必需,它的值是逗號分隔的一個字符串,代表服務器支持的全部跨域請求的方法。注意,返回的是全部支持的方法,而不單是瀏覽器請求的那個方法。這是爲了不屢次」預檢」請求。

  • Access-Control-Allow-Headers:若是瀏覽器請求包括Access-Control-Request-Headers字段,則Access-Control-Allow-Headers字段是必需的。它也是一個逗號分隔的字符串,代表服務器支持的全部頭信息字段,不限於瀏覽器在」預檢」中請求的字段。

  • Access-Control-Max-Age:該字段可選,用來指定本次預檢請求的有效期,單位爲秒。上面結果中,有效期是20天(1728000秒),即容許緩存該條迴應1728000秒(即20天),在此期間,不用發出另外一條預檢請求。

一旦服務器經過了」預檢」請求,之後每次瀏覽器正常的CORS請求,就都跟簡單請求同樣,會有一個Origin頭信息字段。服務器的迴應,也都會有一個Access-Control-Allow-Origin頭信息字段。

簡單請求

對於簡單的跨域請求或者經過了預檢的請求,瀏覽器會自動在請求的頭信息加上Origin字段,表示本次請求來自哪一個源(協議 + 域名 + 端口),服務端會獲取到這個值,而後判斷是否贊成此次請求並返回。典型的請求頭尾:

// 請求
GET /cors HTTP/1.1
Origin: http://api.qiutc.me
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

若是服務端容許,在返回的頭信息中會多出幾個字段:

// 返回
Access-Control-Allow-Origin: http://api.qiutc.me
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: Info
Content-Type: text/html; charset=utf-8
  • Access-Control-Allow-Origin:必須。它的值是請求時Origin字段的值或者 *,表示接受任意域名的請求。

  • Access-Control-Allow-Credentials:可選。它的值是一個布爾值,表示是否容許發送Cookie。默認狀況下,Cookie不包括在CORS請求之中。設爲true,即表示服務器明確許可,Cookie能夠包含在請求中,一塊兒發給服務器。

再須要發送cookie的時候還須要注意要在AJAX請求中打開withCredentials屬性:var xhr = new XMLHttpRequest(); xhr.withCredentials = true;

須要注意的是,若是要發送Cookie,Access-Control-Allow-Origin就不能設爲*,必須指定明確的、與請求網頁一致的域名。同時,Cookie依然遵循同源政策,只有用服務器域名設置的Cookie纔會上傳,其餘域名的Cookie並不會上傳,且原網頁代碼中的document.cookie也沒法讀取服務器域名下的Cookie。

  • Access-Control-Expose-Headers:可選。CORS請求時,XMLHttpRequest對象的getResponseHeader()

法只能拿到6個基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-

Modified、Pragma。若是想拿到其餘字段,就必須在Access-Control-Expose-Headers裏面指定。上面的例子指定,getResponseHeader('Info')能夠返回Info字段的值。

若是服務端拒絕了調用,即不會帶上 Access-Control-Allow-Origin 字段,瀏覽器發現這個跨域請求的返回頭信息沒有該字段,就會拋出一個錯誤,會被 XMLHttpRequestonerror 回調捕獲到。這種錯誤沒法經過 HTTP 狀態碼判斷,由於迴應的狀態碼有多是200。

postMessage

window.postMessage 是一個用於安全的使用跨源通訊的方法。一般,不一樣頁面上的腳本當且僅當執行它們的頁面所處的位置使用相同的協議(一般都是 http)、相同的端口(http默認使用80端口)和相同的主機(兩個頁面的 document.domain 的值相同)只在這種狀況下被容許互相訪問。 而window.postMessage 提供了一個受控的機制來安全地繞過這一限制。其函數原型以下:

windowObj.postMessage(message, targetOrigin);
  • windowObj: 接受消息的 Window 對象。

  • message: 在最新的瀏覽器中能夠是對象。

  • targetOrigin: 目標的源,* 表示任意。

調用postMessage方法的window對象是指要接收消息的那一個window對象,該方法的第一個參數message爲要發送的消息,類型只能爲字符串;第二個參數targetOrigin用來限定接收消息的那個window對象所在的域,若是不想限定域,可使用通配符 * 。須要接收消息的window對象,但是經過監聽自身的message事件來獲取傳過來的消息,消息內容儲存在該事件對象的data屬性中。上面所說的向其餘window對象發送消息,其實就是指一個頁面有幾個框架的那種狀況,由於每個框架都有一個window對象。在討論第種方法的時候,咱們說過,不一樣域的框架間是能夠獲取到對方的window對象的,雖然沒什麼用,可是有一個方法是可用的-window.postMessage。下面看一個簡單的示例,有兩個頁面:

//在主頁面中獲取子頁面的句柄

var iframe =document.getElementById('iframe');
var iframeWindow = iframe.contentWindow;

//向子頁面發送消息
iframeWindow.postMessage("I'm message from main page.");
//在子頁面中監聽獲取消息
window.onmessage = function(e) {
    e = e || event;
    console.log(e.data);
}

Proxy:服務端跨域

使用代理方式跨域更加直接,由於SOP的限制是瀏覽器實現的。若是請求不是從瀏覽器發起的,就不存在跨域問題了。使用本方法跨域步驟以下:

  • 把訪問其它域的請求替換爲本域的請求

  • 本域的請求是服務器端的動態腳本負責轉發實際的請求

不過筆者在本身的開發實踐中發現目前服務端跨域仍是頗有意義的,特別當咱們但願從不支持CORS或者JSONP的服務端獲取數據的時候,每每只能經過跨域請求。

Fetch

JavaScript 經過XMLHttpRequest(XHR)來執行異步請求,這個方式已經存在了很長一段時間。雖然說它頗有用,但它不是最佳API。它在設計上不符合職責分離原則,將輸入、輸出和用事件來跟蹤的狀態混雜在一個對象裏。並且,基於事件的模型與最近JavaScript流行的Promise以及基於生成器的異步編程模型不太搭。新的 Fetch API打算修正上面提到的那些缺陷。 它向JS中引入和HTTP協議中一樣的原語。具體而言,它引入一個實用的函數 fetch() 用來簡潔捕捉從網絡上檢索一個資源的意圖。Fetch 規範 的API明確了用戶代理獲取資源的語義。它結合ServiceWorkers,嘗試達到如下優化:

  • 改善離線體驗

  • 保持可擴展性

而與jQuery相比, fetch 方法與 jQuery.ajax() 的主要區別在於:

  • fetch()方法返回的Promise對象並不會在HTTP狀態碼爲404或者500的時候自動拋出異常,而須要用戶進行手動處理

  • 默認狀況下,fetch並不會發送任何的本地的cookie到服務端,注意,若是服務端依靠Session進行用戶控制的話要默認開啓Cookie

Installation & Polyfill

window.fetch是基於XMLHttpRequest的瀏覽器的統一的封裝,針對老的瀏覽器可使用Github的這個polypill。fetch基於ES6的Promise,在舊的瀏覽器中首先須要引入Promise的polypill,能夠用這個:

$ bower install es6-promise

對於fetch的引入,能夠用bower或者npm:

$ bower install fetch
$ npm install whatwg-fetch --save

若是是基於Webpack的項目,能夠直接在Webpack的config文件中引入這種polyfill:

plugins: [
  new webpack.ProvidePlugin({
    'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch'
  })
]

這個插件的配置主要依靠imports-loaderexports-loader,所以也須要導入它們:

$ npm i imports-loader exports-loader -S

若是感受這種方式比較麻煩,也可使用 isomorphic-fetch

npm install --save isomorphic-fetch es6-promise
bower install --save isomorphic-fetch es6-promise

使用的時候也很是方便:

require('es6-promise').polyfill();
require('isomorphic-fetch');
fetch('//offline-news-api.herokuapp.com/stories')
    .then(function(response) {
        if (response.status >= 400) {
            throw new Error("Bad response from server");
        }
        return response.json();
    })
    .then(function(stories) {
        console.log(stories);
    });

從筆者本身的體驗中,仍是很是推薦使用isomorphic-fetch,其一大優點在於可以在node裏直接進行單元測試與接口可用性測試。老實說筆者以前用Mocha進行帶真實網絡請求的測試時仍是比較不方便的,每每須要在瀏覽器或者phatomjs中進行,而且須要額外的HTML代碼。而在筆者的model.test.js文件中,只須要直接使用babel-node model.test.js 便可以獲取真實的網絡請求,這樣能夠將網絡測試部分與UI相剝離。

Basic Usage:基本使用

假設fetch已經被掛載到了全局的window目錄下。

// Simple response handling

fetch('/some/url').then(function(response) {



}).catch(function(err) {

    // Error :(

});

// Chaining for more "advanced" handling

fetch('/some/url').then(function(response) {

    return //...

}).then(function(returnedValue) {

    // ...

}).catch(function(err) {

    // Error :(

});

Request:請求構造

Request對象表明了一次fetch請求中的請求體部分,你能夠自定義Request對象:

A Request instance represents the request piece of a fetch call. By passingfetch a Request you can make advanced and customized requests:

  • method - 使用的HTTP動詞,GET, POST, PUT, DELETE, HEAD

  • url - 請求地址,URL of the request

  • headers - 關聯的Header對象

  • referrer - referrer

  • mode - 請求的模式,主要用於跨域設置,cors, no-cors, same-origin

  • credentials - 是否發送Cookie omit, same-origin

  • redirect - 收到重定向請求以後的操做,follow, error, manual

  • integrity - 完整性校驗

  • cache - 緩存模式(default, reload, no-cache)

var request = new Request('/users.json', {

    method: 'POST', 

    mode: 'cors', 

    redirect: 'follow',

    headers: new Headers({

        'Content-Type': 'text/plain'

    })

});



// Now use it!

fetch(request).then(function() { /* handle response */ });
fetch('/users.json', {

    method: 'POST', 

    mode: 'cors', 

    redirect: 'follow',

    headers: new Headers({

        'Content-Type': 'text/plain'

    })

}).then(function() { /* handle response */ });

URI Encode

注意,fetch方法是自動會將URI中的雙引號進行編碼的,若是在URI中存入了部分JSON,有時候會出現意想不到的問題,譬如咱們以GET方法訪問以下的URI:

[GET] http://api.com?requestData={"p":"q"}

那麼fetch會自動將雙引號編碼,變成:

[GET] http://api.com?requestData={%22p%22:%22q%22}

那麼這樣一個請求傳入到Spring MVC中時是會引起錯誤的,即URI對象構造失敗這個很噁心的錯誤。筆者沒有看過源代碼,不過猜測會不會是Spring MVC看到{這個字符沒有被編碼,所以默認沒有進行解碼,結果沒想到後面的雙引號被編碼了,爲了不這個無厘頭的錯誤,筆者建議是對URI的Query Parameter部分進行統一的URI編碼:

//將requestData序列化爲JSON
var requestDataString = encodeURIComponent(JSON.stringify(requestData).replace(/%22/g, "\""));
//將字符串連接
const packagedRequestURL = `${Model.BASE_URL}${path}?requestData=${requestDataString}&action=${action}`;

Headers:自定義請求頭

// Create an empty Headers instance

var headers = new Headers();



// Add a few headers

headers.append('Content-Type', 'text/plain');

headers.append('X-My-Custom-Header', 'CustomValue');



// Check, get, and set header values

headers.has('Content-Type'); // true

headers.get('Content-Type'); // "text/plain"

headers.set('Content-Type', 'application/json');



// Delete a header

headers.delete('X-My-Custom-Header');



// Add initial values

var headers = new Headers({

'Content-Type': 'text/plain',

'X-My-Custom-Header': 'CustomValue'

});

常見的請求方法有: append, has, get, set以及 delete

var request = new Request('/some-url', {

    headers: new Headers({

        'Content-Type': 'text/plain'

    })

});



fetch(request).then(function() { /* handle response */ });

POST & body:POST請求

fetch('/users', {
  method: 'post',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json'
  },
  body: JSON.stringify({
    name: 'Hubot',
    login: 'hubot',
  })
})

File Upload:文件上傳

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'post',
  body: data
})

Cookies

若是須要設置fetch自動地發送本地的Cookie,須要將credentials設置爲same-origin:

fetch('/users', {
  credentials: 'same-origin'
})

該選項會以相似於XMLHttpRequest的方式來處理Cookie,不然,可能由於沒有發送Cookie而致使基於Session的認證出錯。能夠將credentials的值設置爲include來在CORS狀況下發送請求。

fetch('https://example.com:1234/users', {
  credentials: 'include'
})

Response:響應處理

fetchthen函數中提供了一個Response對象,即表明着對於服務端返回值的封裝,你也能夠在Mock的時候自定義Response對象,譬如在你須要使用Service Workers的狀況下,在Response中,你能夠做以下配置:

  • type - basic, cors

  • url

  • useFinalURL - 是否爲最終地址

  • status - 狀態碼 (ex: 200, 404, etc.)

  • ok - 是否成功響應 (status in the range 200-299)

  • statusText - status code (ex: OK)

  • headers - 響應頭

// Create your own response for service worker testing

// new Response(BODY, OPTIONS)

var response = new Response('.....', {

ok: false,

status: 404,

url: '/'

});



// The fetch's `then` gets a Response instance back

fetch('/')

.then(function(responseObj) {

console.log('status: ', responseObj.status);

});

The Response also provides the following methods:

  • clone() - Creates a clone of a Response object.

  • error() - Returns a new Response object associated with a network error.

  • redirect() - Creates a new response with a different URL.

  • arrayBuffer() - Returns a promise that resolves with an ArrayBuffer.

  • blob() - Returns a promise that resolves with a Blob.

  • formData() - Returns a promise that resolves with a FormData object.

  • json() - Returns a promise that resolves with a JSON object.

  • text() - Returns a promise that resolves with a USVString (text).

Handling HTTP error statuses:處理HTTP錯誤狀態

function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response
  } else {
    var error = new Error(response.statusText)
    error.response = response
    throw error
  }
}

function parseJSON(response) {
  return response.json()
}

fetch('/users')
  .then(checkStatus)
  .then(parseJSON)
  .then(function(data) {
    console.log('request succeeded with JSON response', data)
  }).catch(function(error) {
    console.log('request failed', error)
  })

Handling JSON:處理JSON響應

fetch('https://davidwalsh.name/demo/arsenal.json').then(function(response) { 

// Convert to JSON

return response.json();

}).then(function(j) {

// Yay, `j` is a JavaScript object

console.log(j); 

});

Handling Basic Text/HTML Response:處理文本響應

fetch('/next/page')

  .then(function(response) {

    return response.text();

  }).then(function(text) { 

  // <!DOCTYPE ....

  console.log(text); 

  });

Blob Responses

若是你但願經過fetch方法來載入一些相似於圖片等資源:

fetch('flowers.jpg')

    .then(function(response) {

       return response.blob();

    })

    .then(function(imageBlob) {

       document.querySelector('img').src = URL.createObjectURL(imageBlob);

});

blob()方法會接入一個響應流而且一直讀入到結束。

Best Practice

筆者在本身的項目中封裝了一個基於ES6 Class的基本的模型請求類,代碼地址

/**

 * Created by apple on 16/5/3.

 */

//自動進行全局的ES6 Promise的Polyfill

require('es6-promise').polyfill();

require('isomorphic-fetch');





/**

 * @function 基礎的模型類,包含了基本的URL定義

 */

export default class Model {





    //默認的基本URL路徑

    static BASE_URL = "/";



    //默認的請求頭

    static headers = {};



    /**

     * @function 默認構造函數

     */

    constructor() {



        this._checkStatus = this._checkStatus.bind(this);



        this._parseJSON = this._parseJSON.bind(this);



        this._parseText = this._parseText.bind(this);



        this._fetchWithCORS = this._fetchWithCORS.bind(this);





    }



    /**

     * @function 檢測返回值的狀態

     * @param response

     * @returns {*}

     */

    _checkStatus(response) {



        if (response.status >= 200 && response.status < 300) {

            return response

        } else {

            var error = new Error(response.statusText);

            error.response = response;

            throw error

        }

    }



    /**

     * @function 解析返回值中的Response爲JSON形式

     * @param response

     * @returns {*}

     */

    _parseJSON(response) {



        if (!!response) {



            return response.json();

        }

        else {

            return undefined;

        }



    }



    /**

     * @function 解析TEXT性質的返回

     * @param response

     * @returns {*}

     */

    _parseText(response) {



        if (!!response) {



            return response.text();

        }

        else {

            return undefined;

        }



    }



    /**

     * @function 封裝好的跨域請求的方法

     * @param packagedRequestURL

     * @returns {*|Promise.<TResult>}

     * @private

     */

    _fetchWithCORS(packagedRequestURL, contentType) {



        return fetch(packagedRequestURL, {

            mode: "cors", headers: Model.headers

        })

            .then(this.checkStatus, (error)=> {

                return error;

            })

            .then(contentType === "json" ? this._parseJSON : this._parseText, (error)=> {

                return error;

            });

    }



    /**

     * @function 利用get方法發起請求

     * @param path 請求的路徑(包括路徑參數)

     * @param requestData 請求的參數

     * @param action 請求的類型

     * @param contentType 返回的類型

     * @returns {Promise.<TResult>|*} Promise.then((data)=>{},(error)=>{});

     */

    get({BASE_URL=Model.BASE_URL, path="/", action="GET", contentType="json"}) {



        //封裝最終待請求的字符串

        const packagedRequestURL = `${BASE_URL}${(path)}?action=${action}`;



        //以CORS方式發起請求

        return this._fetchWithCORS(packagedRequestURL, contentType);



    }



    /**

     * @function 利用get方法與封裝好的QueryParams形式發起請求

     * @param path 請求的路徑(包括路徑參數)

     * @param requestData 請求的參數

     * @param action 請求的類型

     * @returns {Promise.<TResult>|*} Promise.then((data)=>{},(error)=>{});

     */

    getWithQueryParams({BASE_URL=Model.BASE_URL, path="/", queryParams={}, action="GET", contentType="json"}) {





        //初始化查詢字符串

        let queryString = "";



        //根據queryParams構造查詢字符串

        for (let key in queryParams) {



            //注意,請求參數必須進行URI格式編碼,若是是JSON等特殊格式須要在服務端進行解碼

            queryString += `${key}=${encodeURIComponent(queryParams[key])}&`;



        }



        //將查詢字符串進行編碼

        let encodedQueryString = (queryString);



        //封裝最終待請求的字符串

        const packagedRequestURL = `${BASE_URL}${path}?${encodedQueryString}action=${action}`;



        //以CORS方式發起請求

        return this._fetchWithCORS(packagedRequestURL, contentType);



    }



    /**

     * @function 利用get方法與封裝好的RequestData形式發起請求

     * @param path 請求的路徑(包括路徑參數)

     * @param requestData 請求的參數

     * @param action 請求的類型

     * @returns {Promise.<TResult>|*} Promise.then((data)=>{},(error)=>{});

     */

    getWithRequestData({path="/", requestData={}, action="GET", contentType="json"}) {



        //將requestData序列化爲JSON

        //注意要對序列化後的數據進行URI編碼

        var requestDataString = encodeURIComponent(JSON.stringify(requestData));



        //將字符串連接

        const packagedRequestURL = `${Model.BASE_URL}${path}?requestData=${requestDataString}&action=${action}`;



        return this._fetchWithCORS(packagedRequestURL, contentType);



    }



    /**

     * @function 考慮到將來post會有不一樣的請求方式,所以作區分處理

     * @param path

     * @param requestData

     * @param action

     * @returns {Promise.<TResult>|*}

     */

    postWithRequestData({path="/", requestData={}, action="POST", contentType="json"}) {



        //將requestData序列化爲JSON

        //注意要對序列化後的數據進行URI編碼

        var requestDataString = encodeURIComponent(JSON.stringify(requestData));



        //將字符串連接

        const packagedRequestURL = `${Model.BASE_URL}${path}?requestData=${requestDataString}&action=${action}`;



        return this._fetchWithCORS(packagedRequestURL, contentType);

    }



    put({path="/", requestData={}, action="put", contentType="json"}) {



    }





    delete({path="/", requestData={}, action="DELETE", contentType="json"}) {



    }



}





Model.testData = {};



Model.testData.error = {};

相關文章
相關標籤/搜索