一比一還原axios原始碼(三)—— 錯誤處理
前面的章節我們已經可以正確的處理正確的請求,並且通過處理header、body,以及加入了promise,讓我們的程式碼更像axios了。這一章我們一起來處理ajax請求中的錯誤。
一、錯誤處理
首先我們要知道錯誤有哪些型別,通常我們遇到的錯誤有以下幾種:網路錯誤、超時錯誤和非200狀態碼錯誤。其實都不復雜我們來看下:
1、網路異常
網路異常,會觸發XMLHttpRequest的onerror事件,所以我們只需要加上就可以了:
request.onerror = function handleError() { reject(new Error("Network Error")); };
2、超時錯誤
XMLHttpRequest物件允許配置timeout引數,預設是0,也就是永遠不會超時,所以我們的程式碼這樣處理就可以了,注意,這裡的config.timeout的config,實際上就是我們傳入的配置引數,只不過這裡引用了一下,包括後續的完整的實現其他api的部分,有很多其實都是對原生api的一個簡單的對映和轉換,後面再說:
if (config.timeout) { request.timeout = config.timeout; } request.ontimeout = function handleTimeout() { reject(new Error(`Timeout of ${config.timeout} ms exceeded`)); };
3、非200狀態碼
如果請求報錯。那麼XMLHttpRequest的status屬性會返回0,所以我們需要額外判斷下status,中斷後續的程式碼:
我們在onreadystatechange回撥中加入status的判斷。然後我們改變一下丟擲response的方式,使用一個函式來處理:
return new Promise((resolve, reject) => { var request = new XMLHttpRequest(); config.headers = processHeaders(config.headers || {}, config.data); config.data= transformRequest(config.data); request.open( config.method.toUpperCase(), buildURL(config.url, config.params, config.paramsSerializer), true ); request.onreadystatechange = function handleLoad() { if (request.readyState !== 4) { return; } if (request.status === 0) { return; } const responseHeaders = parseHeaders(request.getAllResponseHeaders()); const responseData = config.responseType && config.responseType !== "text" ? request.response : request.responseText; const response = { data: responseData, status: request.status, statusText: request.statusText, headers: responseHeaders, config, request, }; handleResponse(response); };
// 加了這個方法來集中處理response function handleResponse(response) { if (response.status >= 200 && response.status < 300) { resolve(response); } else { reject(new Error(`Request failed with status code ${response.status}`)); } }
很簡單~~。錯誤處理完成到這裡實際上就完成了,簡單總結下,攔截了readystatechange事件中的status並根據對應的情況,處理response是resolve還是reject。然後根據timeout和error事件來丟擲對應的錯誤。
但是到這裡還沒真正的完成錯誤的處理,因為我們在錯誤處理的時候僅僅丟擲了錯誤資訊,沒辦法處理一些額外的資料,比如請求配置、響應物件等。我們下面來完成一個Error類,集中處理這些情況,讓我們的程式碼更健壯。
二、完成AxiosError
首先我們在core資料夾下建立一個createError檔案:
export default function createError(message, config, code, request, response) { var error = new Error(message); return enhanceError(error, config, code, request, response); }
我們來看上面的程式碼,整個createError方法,返回了報錯資訊、配置、狀態碼、請求和響應內容。然後我們實際執行返回的是enhanceError方法。我們再來在core資料夾下建立一個enhanceError檔案:
export default function enhanceError(error, config, code, request, response) { error.config = config; if (code) { error.code = code; } error.request = request; error.response = response; error.isAxiosError = true; error.toJSON = function toJSON() { return { // Standard message: this.message, name: this.name, // Microsoft description: this.description, number: this.number, // Mozilla fileName: this.fileName, lineNumber: this.lineNumber, columnNumber: this.columnNumber, stack: this.stack, // Axios config: this.config, code: this.code, status: this.response && this.response.status ? this.response.status : null, }; }; return error; }
上面的程式碼,很簡單,把所有的資訊繫結到error物件上,最後再返回error即可。其中需要注意的是,error.toJSON這個東西,它實際上做的就是當你在外層呼叫error的toJSON方法的時候,會返回這個更改後的方法。相當於改寫了這個物件上的toJSON方法。
比如我們列印下這個東西:
console.log( createError(timeoutErrorMessage, config, "ETIMEDOUT", request).toJSON() );
就是enhanceError返回的那個。OK,到此我們已經寫好了createError方法(其實我是從原始碼複製過來的,一點修改都沒有)。那麼我們需要修改下之前錯誤處理中的程式碼,至於具體修改的方法,就當留個作業了。大家也可以去專案中的c3分支檢視。
到此,我們處理完了錯誤資訊,添加了新的createError方法。到目前為止,其實程式碼都還不是真正的axios,為什麼這麼說呢,到現在,我們只是實現了其中的功能,但是其實還不是真正的axios原始碼的組織方式,我們下一章,就來擴充套件整個zaking-axios,修改檔案的相關性,建立Axios類等,來完成更多的功能。