使用XHR發送一個json請求一般是這樣: 使用fetch的實例: Fetch參數 fetch(input [,init]) input(必須) 定義要獲取的資源(請求地址) init(可選) 參數 | 描述 method 請求使用的方法,如GET、POST headers http請求頭(user ...
使用XHR發送一個json請求一般是這樣:
1 const xhr = new XMLHttpRequest() 2 xhr.open('Get', url) 3 xhr.responseType = 'json' 4 5 xhr.onload = () => { 6 console.log(xhr.response) 7 } 8 9 xhr.onerror = () => { 10 console.log('error') 11 } 12 13 xhr.send()
使用fetch的實例:
1 fetch(url).then(response => response.json()) 2 .then(data => console.log(data)) 3 .catch(e => console.log('error', e))
Fetch參數
fetch(input [,init])
input(必須) 定義要獲取的資源(請求地址)
init(可選)
參數 | 描述
method 請求使用的方法,如GET、POST
headers http請求頭(user-Agent, Cookie)
body 請求的body信息
mode
fetch可以設置不同的模式使得請求有效. 模式可在fetch方法的第二個參數對象中定義.
可定義的模式如下:
- same-origin: 表示同域下可請求成功; 反之, 瀏覽器將拒絕發送本次fetch, 同時拋出錯誤 “TypeError: Failed to fetch(…)”.
- cors: 表示同域和帶有CORS響應頭的跨域下可請求成功. 其他請求將被拒絕.
- cors-with-forced-preflight: 表示在發出請求前, 將執行preflight檢查.
- no-cors: 常用於跨域請求不帶CORS響應頭場景, 此時響應類型為 “opaque”.
除此之外, 還有兩種不太常用的mode類型, 分別是 navigate , websocket , 它們是 HTML標準 中特殊的值, 這裡不做詳細介紹.
credentials
omit(預設值,預設為該值)、same-origin(同源,便是同域請求才發送cookie)、include(任何請求都帶cookie)
cache
- default(表示fetch請求之前將檢查下http的緩存)
- no-store(表示fetch請求將完全忽略http緩存的存在,這意味著請求之前將不再檢查下http的緩存, 拿到響應後, 它也不會更新http緩存)
- no-cache(如果存在緩存,那麼fetch將發送一個條件查詢request和一個正常的request, 拿到響應後,它會更新http緩存)
- reload(表示fetch請求之前將忽略http緩存的存在, 但是請求拿到響應後,它將主動更新http緩存)
- force-cache(表示fetch請求不顧一切的依賴緩存, 即使緩存過期了,它依然從緩存中讀取. 除非沒有任何緩存, 那麼它將發送一個正常的request)
- only-if-cached(表示fetch請求不顧一切的依賴緩存, 即使緩存過期了,它依然從緩存中讀取. 如果沒有緩存, 它將拋出網路錯誤(該設置只在mode為”same-origin”時有效).
如果fetch請求的header里包含 If-Modified-Since, If-None-Match, If-Unmodified-Since, If-Match, 或者 If-Range 之一, 且cache的值為 default , 那麼fetch將自動把 cache的值設置為 "no-store"
Fetch - response type
一個fetch請求的相應類型(response.type)為如下三種之一:
- baisc (同域下,相應類型為basic)
- cors (同樣是跨域下, 如果伺服器返回了CORS響應頭, 那麼響應類型將為 “cors”. 此時響應頭中除 Cache-Control , Content-Language , Content-Type , Expores , Last-Modified 和 Progma 之外的欄位都不可見.)
- opaque ( 跨域下, 伺服器沒有返回CORS響應頭, 響應類型為 “opaque”. 此時我們幾乎不能查看任何有價值的信息, 比如不能查看response, status, url等等等等.)
註意: 無論是同域還是跨域, 以上 fetch 請求都到達了伺服器.
Fetch 常見坑
1.Fetch 請求預設是不帶 cookie 的,需要設置 fetch(url, {credentials: 'include'})
預設情況下, fetch 不會從服務端發送或接收任何 cookies, 如果站點依賴於維護一個用戶會話,則導致未經認證的請求(要發送 cookies,必鬚髮送憑據頭)
2.伺服器返回 400,500 錯誤碼時並不會 reject,只有網路錯誤這些導致請求不能完成時,fetch 才會被 reject。
當接收到一個代表錯誤的 HTTP 狀態碼時,從 fetch()返回的 Promise 不會被標記為 reject, 即使該 HTTP 響應的狀態碼是 404 或 500。相反,它會將 Promise 狀態標記為 resolve (但是會將 reolve 的返回值的 ok 屬性設置為 false, 想要精確判斷fetch()是否成功,需要包含 promise resolved 的情況,此時再判斷 Response.ok 是不是為 true。HTTP狀態碼為200-299是才會設置為true), 僅當網路故障時或請求被阻止時,才會標記為 rejec
使用Fetch封裝request方法
http.js
1 import 'whatwg-fetch'; 2 3 const netErrorStatu = 1; // 網路錯誤 4 const serverErrorStatu = 2; // 伺服器錯誤 5 const formatErrorStatu = 3; // 數據格式錯誤 6 const logicErrorStatu = 4; // 業務邏輯錯誤 7 8 const errorMsg = { 9 [netErrorStatu]: '網路錯誤', 10 [serverErrorStatu]: '伺服器錯誤', 11 [formatErrorStatu]: '數據格式錯誤', 12 [logicErrorStatu]: '業務邏輯錯誤' 13 }; 14 15 class CustomFetchError { 16 constructor(errno, data) { 17 this.errno = errno; 18 this.msg = errorMsg[errno]; 19 this.data = data; 20 } 21 } 22 23 export function buildQuery(data) { 24 const toString = Object.prototype.toString; 25 26 const res = Object.entries(data).reduce((pre, [key, value]) => { 27 let newValue; 28 29 if (Array.isArray(value) || toString.call(value) === '[object Object]') { 30 newValue = JSON.stringify(value); 31 } else { 32 newValue = (value === null || value === undefined) ? '' : value; 33 } 34 35 pre.push(`${key}=${encodeURIComponent(newValue)}`); 36 37 return pre; 38 }, []); 39 40 return res.join('&'); 41 } 42 43 export async function request(input, opt) { 44 // 設置請求預設帶cookie 45 const init = Object.assign({ 46 credentials: 'include' 47 }, opt); 48 49 let res; 50 // 僅當網路故障時或請求被阻止時,才會標記為 rejec 51 try { 52 res = await fetch(input, init); 53 } catch (e) { 54 throw new CustomFetchError(netErrorStatu, e); 55 } 56 // 僅HTTP狀態碼為200-299是才會設置為true 57 if (!res.ok) { 58 throw new CustomFetchError(serverErrorStatu, res); 59 } 60 61 let data; 62 // fetch()請求返回的response是Stream對象,調用response.json時由於非同步讀取流對象所以返回的是一個Promise對象 63 try { 64 data = await res.json(); 65 } catch (e) { 66 throw new CustomFetchError(formatErrorStatu, e); 67 } 68 // 根據和後臺的約定設置的錯誤處理 69 if (!data || data.errno !== 0) { 70 throw new CustomFetchError(logicErrorStatu, data); 71 } 72 73 return data.data; 74 } 75 76 export function get(url, params = {}, opt = {}) { 77 const init = Object.assign({ 78 method: 'GET' 79 }, opt); 80 81 // ajax cache 82 Object.assign(params, { 83 timestamp: new Date().getTime() 84 }); 85 86 const paramsStr = buildQuery(params); 87 88 const urlWithQuery = url + (paramsStr ? `?${paramsStr}` : ''); 89 90 return request(urlWithQuery, init); 91 } 92 93 export function post(url, params = {}, opt = {}) { 94 const headers = { 95 'Content-Type': 'application/x-www-form-urlencoded' 96 }; 97 98 const init = Object.assign({ 99 method: 'POST', 100 body: buildQuery(params), 101 headers 102 }, opt); 103 104 return request(url, init); 105 } 106 107 export default { 108 request, 109 get, 110 post 111 };
requset.js
1 import { notification } from 'antd'; 2 import Loading from 'components/Loading'; 3 import { LOGIN_URL } from 'constants/basic'; 4 import * as http from './http'; 5 6 const loading = Loading.newInstance(); 7 8 async function request(method, url, params, opt = {}, httpOpt) { 9 /** 10 * needLoading 是否添加loading圖片 11 * checkAccount 驗證未登陸是否跳登陸頁面 12 * showErrorMsg 是都顯示通用錯誤提示 13 */ 14 const { 15 needLoading = true, 16 checkAccount = true, 17 showErrorMsg = true 18 } = opt; 19 20 if (needLoading) { 21 loading.add(); 22 } 23 24 let res; 25 26 try { 27 res = await http[method](url, params, httpOpt); 28 } catch (e) { 29 if (checkAccount && e.errno === 4 && e.data.errno === 10000) { 30 location.href = LOGIN_URL; 31 } 32 33 if (showErrorMsg) { 34 notification.error({ 35 message: '提示信息', 36 description: e.errno === 4 ? e.data.msg : e.msg 37 }); 38 } 39 40 throw e; 41 } finally { 42 if (needLoading) { 43 loading.remove(); 44 } 45 } 46 47 return res; 48 } 49 50 export function get(...arg) { 51 return request('get', ...arg); 52 } 53 54 export function post(...arg) { 55 return request('post', ...arg); 56 }
github的代碼地址: https://github.com/haozhaohang/library