最近用fetch與後臺進行數據交互出現各種問題,尤其是跨域問題很讓人頭疼,今天找到一篇文章,覺得不錯,向大家推薦一下
同源策略
可謂同源?URL由協議、域名、端口和路徑組成,如果兩個URL的協議、域名和端口相同,則表示他們同源。瀏覽器的同源策略,限制了來自不同源的"document"或腳本,對當前"document"讀取或設置某些屬性,即從一個域上加載的腳本不允許訪問另外一個域的文檔屬性。比如一個惡意網站的頁面通過iframe嵌入了銀行的登錄頁面(二者不同源),如果沒有同源限制,惡意網頁上的javascript腳本就可以在用戶登錄銀行的時候獲取用戶名和密碼。所謂道高一尺魔高一丈,雖然瀏覽器以同源策略限制了我們隨意請求資源,但是從這個策略出現開始就有很多各種各樣的Hacker技巧來。
JSONP
JSONP是較爲常用的一種跨域方式,不受到瀏覽器兼容性的限制,但是因爲它只能以GET動詞進行請求,這樣就破壞了標準的REST風格,比較醜陋。JSONP本質上是利用 <script>
標籤的跨域能力實現跨域數據的訪問,請求動態生成的JavaScript腳本同時帶一個callback函數名作爲參數。其中callback函數本地文檔的JavaScript函數,服務器端動態生成的腳本會產生數據,並在代碼中以產生的數據爲參數調用
callback函數。當這段腳本加載到本地文檔時,callback函數就被調用。
(1)瀏覽器端構造請求地址
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
是我們默認註冊的回調函數,注意,該函數名需要全局唯一,該函數接收服務端返回的數據作爲參數,而函數內容就是對於該參數的處理。
(2)服務端構造返回值
在接受到瀏覽器端 script 的請求之後,從url的query的callbackName獲取到回調函數的名字,例子中是 resolveJson
。
然後動態生成一段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
字段,瀏覽器發現這個跨域請求的返回頭信息沒有該字段,就會拋出一個錯誤,會被 XMLHttpRequest
的 onerror
回調捕獲到。這種錯誤無法通過
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-loader
與 exports-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 passing fetch
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
- 是否發送Cookieomit
,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:響應處理
在 fetch
的 then
函數中提供了一個 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 = {};