到此,我們完成了axios的絕大部分的功能,接下來我們來補全一下其他的小功能。
一、withCredentials
這個引數可以可以表明是否是一個跨域的請求。那這個的使用場景是啥呢?就是我們在同域的請求的情況下,是會預設攜帶cookie的,跨域的話就不會攜帶cookie,如果我們想要跨域請求並攜帶cookie,那麼就需要這個引數了。當然這個實現非常簡單:
if (!utils.isUndefined(config.withCredentials)) { request.withCredentials = !!config.withCredentials; }
嗯,就這樣就完了。
然後我們需要建立一個作為接受跨域請求的server2.js作為跨域訪問的伺服器。程式碼可以在gitHub上看哦。就不多說了。
二、XSRF 防禦
就是跨站請求偽造,登入信任的A網站後會產生該使用者的信任cookie,由於瀏覽器在傳送請求的時候會自動攜帶cookie,如果使用者在沒有登出的情況下登入黑客網站,那麼就會把帶有信任的cookie傳遞給黑客網站,此時黑客網站拿到你的使用者cookie就可以偽造使用者登入A網站了。
XSRF的防禦手段有很多,比如referer,但是referer也是可以偽造的,所以杜絕此類攻擊的一種方式是伺服器端要求每次請求都包含一個token
,這個token
不在前端生成,而是在我們每次訪問站點的時候生成,並通過set-cookie
的方式種到客戶端,然後客戶端傳送請求的時候,從cookie
中對應的欄位讀取出token
,然後新增到請求headers
中。這樣服務端就可以從請求headers
中讀取這個token
並驗證,由於這個token
是很難偽造的,所以就能區分這個請求是否是使用者正常發起的。
所以在axios中,我們需要自動把這些事情做了,每次傳送請求的時候,從cookie
中讀取對應的token
值,然後新增到請求headers
中。我們允許使用者配置xsrfCookieName
和xsrfHeaderName
,其中xsrfCookieName
表示儲存token
的cookie
名稱,xsrfHeaderName
表示請求headers
中token
對應的header
名稱。
例子就像這樣:
const instance = axios.create({ xsrfCookieName: 'XSRF-TOKEN-D', xsrfHeaderName: 'X-XSRF-TOKEN-D' }) instance.get('/more/get').then(res => { console.log(res) })
那麼接下來我們來看下程式碼的實現:
首先,我們在defaults中新增兩個預設引數:
var defaults = { // ... xsrfCookieName: "XSRF-TOKEN", xsrfHeaderName: "X-XSRF-TOKEN", // ... }
首先啊,我們要做一些判斷,首先要判斷如果是配置 withCredentials
為 true
或者是同域請求,我們才會請求 headers
新增 xsrf
相關的欄位,然後我們在helpers資料夾下建立一個isURLSameOrigin檔案,用來判斷是否是同源,我直接從axios複製過來的,哈哈。程式碼大家自己去看註釋哦。它的核心其實就是建立個a標籤,然後設定屬性為我們傳入的url地址,通過這個DOM,我們就可以獲取到對應url的protocol、host等屬性,然後我們判斷這兩個是否相同就可以了。
然後,我們在建立個cookies檔案,也是在helpers資料夾中,這個cookies主要封裝了一些cookie的讀寫操作。
最後,就是我們的核心邏輯程式碼了,其實很簡單:
if (utils.isStandardBrowserEnv()) { // Add xsrf header var xsrfValue = (config.withCredentials || isURLSameOrigin(config.url)) &&
config.xsrfCookieName ? cookies.read(config.xsrfCookieName) : undefined; if (xsrfValue) { requestHeaders[config.xsrfHeaderName] = xsrfValue; } }
首先判斷是否是瀏覽器環境,如果是的話,就再判斷是否有withCredentials或者是同源的,並且有xsrfCookieName,然後讀取cookie中的xsrfCookieName的值,然後設定給headers即可。其實核心邏輯並不複雜,複雜的是XSRF的概念,和一些它的判斷條件中的方法。
最後,在我們的demo裡,我們還要設定下server.js:
app.use(express.static(__dirname, { setHeaders (res) { res.cookie('XSRF-TOKEN-D', '1234abc') } }))
這樣才可以。
三、上傳下載進度監控
其實上傳和下載的進度監控,都可以通過xhr的原生的物件來進行獲取。這是axios的文件:
我們先來看實現吧,實現起來非常簡單,其實就是傳了個引數,xhr裡呼叫一下:
// Handle progress if needed if (typeof config.onDownloadProgress === 'function') { request.addEventListener('progress', config.onDownloadProgress); } // Not all browsers support upload events if (typeof config.onUploadProgress === 'function' && request.upload) { request.upload.addEventListener('progress', config.onUploadProgress); }
就這麼簡單,完事了。。。。demo的話我就不在這裡佔篇幅了,大家去專案裡看下。
四、Authorization
HTTP 協議中的 Authorization 請求 header 會包含伺服器用於驗證使用者代理身份的憑證,通常會在伺服器返回 401 Unauthorized 狀態碼以及 WWW-Authenticate 訊息頭之後在後續請求中傳送此訊息頭。
axios 庫也允許你在請求配置中配置 auth
屬性,auth
是一個物件結構,包含 username
和 password
2 個屬性。一旦使用者在請求的時候配置這倆屬性,我們就會自動往 HTTP 的 請求 header 中新增 Authorization
屬性,它的值為 Basic 加密串
。 這裡的加密串是 username:password
base64 加密後的結果。
axios文件中是這樣說明的:
我們來看下程式碼實現:
// HTTP basic authentication if (config.auth) { var username = config.auth.username || ""; var password = config.auth.password ? unescape(encodeURIComponent(config.auth.password)) : ""; requestHeaders.Authorization = "Basic " + btoa(username + ":" + password); }
就這麼簡單~~。
五、自定義合法狀態碼
額。。。跟文件一模一樣。。。。那我們在判斷promise是走resolve還是reject的時候,就要額外的處理下了,對了,大家還記得我們實在哪裡執行的了不?還記不記得有個settle檔案?
export default function settle(resolve, reject, response) { var validateStatus = response.config.validateStatus; if (!response.status || !validateStatus || validateStatus(response.status)) { resolve(response); } else { reject( createError( "Request failed with status code " + response.status, response.config, null, response.request, response ) ); } }
嗨(四聲)!就是個判斷!
六、自定義引數序列化
我們先來看文件:
這個東西呢,允許我們自己定義規則解析url後的query引數,大多數情況下用不到吼。
七、baseURL
有些時候,我們會請求某個域名下的多個介面,我們不希望每次傳送請求都填寫完整的 url,希望可以配置一個 baseURL
,之後都可以傳相對路徑。我們一旦配置了 baseURL
,之後請求傳入的 url
都會和我們的 baseURL
拼接成完整的絕對地址,除非請求傳入的 url
已經是絕對地址。
var fullPath = buildFullPath(config.baseURL, config.url); request.open( config.method.toUpperCase(), buildURL(fullPath, config.params, config.paramsSerializer), true );
八、靜態方法擴充套件
額。。。直接看程式碼吧,懶得說了~~~
首先,擴充套件幾個靜態方法:
// Expose all/spread axios.all = function all(promises) { return Promise.all(promises); }; axios.spread = function spread(callback) { return function wrap(arr) { return callback.apply(null, arr); }; }; // Expose isAxiosError axios.isAxiosError = function isAxiosError(payload) { return utils.isObject(payload) && payload.isAxiosError === true; };
然後,擴充套件個getUri方法:
Axios.prototype.getUri = function getUri(config) { config = mergeConfig(this.defaults, config); return buildURL(config.url, config.params, config.paramsSerializer).replace( /^\?/, "" ); };
撒花,完結~