在前一章节,我们实现了ts-axios的Promise封装和构建,完成了请求发送成功和成功获取数据后的处理操作。本篇我们将完成ts-axios面对各种错误时的处理方式。这些错误包括网络延时、请求未找到(非200状态)、服务器返回错误(500状态)等。
一、修改xhr对象
文件位置: /src/xhr.ts
此处为了代码的完整性,我把整个代码片段都贴了出来。整体代码都比较直观,注释也比较详细。
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/header'
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data, url, method = 'get', headers, responseType, timeout } = config
// 创建XHR请求对象的实例
const request = new XMLHttpRequest()
/**如果存在responseType */
if (responseType) {
request.responseType = responseType
}
/**设置请求时间 */
if (timeout) {
request.timeout = timeout
}
/**发送请求 */
request.open(method.toUpperCase(), url, true)
/**处理相应后的数据 */
request.onreadystatechange = function handleLoad() {
if (request.readyState !== 4) {
return
}
if (request.status === 0) {
return
}
const responseHeaders = parseHeaders(request.getAllResponseHeaders())
const responseData = responseType !== 'text' ? request.response : request.responseText
const response: AxiosResponse = {
data: responseData,
status: request.status,
statusText: request.statusText,
headers: responseHeaders,
config,
request
}
handleResponse(response)
}
/**网络错误处理 */
request.onerror = function() {
reject(new Error('Network Error!'))
}
/**处理请求错误 */
request.ontimeout = function() {
reject(new Error(`Timeout of ${timeout} ms exceeded`))
}
/**处理网络请求头 */
Object.keys(headers).forEach(name => {
if (data === null && name.toLowerCase() === 'content-type') {
delete headers[name]
} else {
request.setRequestHeader(name, headers[name])
}
})
request.send(data)
// 处理状态码函数
function handleResponse(response: AxiosResponse): void {
if (response.status >= 200 && response.status < 300) {
resolve(response)
} else {
reject(`Request failed with status code ${response.status}`)
}
}
})
}
二、修改AxiosResponse接口类型,新增timeout属性
文件位置: /src/types/index.ts
export interface AxiosRequestConfig {
url: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
}
三、编写测试用例
import axios from '../../src/index'
axios({
method: 'get',
url: '/error/get1'
})
.then(res => {
console.log(res)
})
.catch(e => {
console.log(e)
})
axios({
method: 'get',
url: '/error/get'
})
.then(res => {
console.log(res)
})
.catch(e => {
console.log(e)
})
setTimeout(() => {
axios({
method: 'get',
url: '/error/get'
})
.then(res => {
console.log(res)
})
.catch(e => {
console.log(e)
})
}, 5000)
在编写用例的时候,第三个测试用例需要我们注意一下。因为它没设置了5000ms后调用,这个用例是为了测试延迟函数调用的,所以我们应该遵循下面的操作流程。
在请求刚开始的时候,将浏览器network面板中网络设置为online,然后这只为offline(5秒内),这样就可以测试超时调用了。
四、优化错误处理
通过上面的步骤,我们实现了对ts-axios错误的相关的处理,但是对于这样的处理来说,显得有些简单,因为我们只是简单的返回一段错误的提示,我们还应该将错误的编号、请求的配置等信息返回,方便我们快速定位错误。
1、新增错误类型
export interface AxiosError extends Error {
isAxiosError: boolean
config: AxiosRequestConfig
code?: string | null
request?: any
response?: AxiosResponse
}
2、创建error处理函数
文件位置 /src/helper/error.ts
import { AxiosRequestConfig, AxiosResponse } from '../types'
export class AxiosError extends Error {
isAxiosError: boolean
config: AxiosRequestConfig
code?: string | null
request?: any
response?: AxiosResponse
constructor(
message: string,
config: AxiosRequestConfig,
code?: string | null,
request?: any,
response?: AxiosResponse
) {
super(message)
this.config = config
this.code = code
this.request = request
this.response = response
this.isAxiosError = true
Object.setPrototypeOf(this, AxiosError.prototype)
}
}
export function createError(
message: string,
config: AxiosRequestConfig,
code?: string | null,
request?: any,
response?: AxiosResponse
): AxiosError {
const error = new AxiosError(message, config, code, request, response)
return error
}
3、重构错误返回值
import { createError } from '../helpers/error'
...
/**网络错误处理 */
request.onerror = function() {
reject(createError('Network Error!', config, null, request))
}
/**处理请求错误 */
request.ontimeout = function() {
reject(createError(`Timeout of ${timeout} ms exceeded`, config, null, request))
}
通过上面对错误处理的优化,我们可以返回更加确切的错误提示了,我们可以以下面的测试用例来检测一下我们优化的成果。
axios({
method: 'get',
url: '/error/timeout',
timeout: 2000
})
.then(res => {
console.log(res)
})
.catch((e: AxiosError) => {
console.log(e.message)
console.log(e.config)
console.log(e.code)
console.log(e.request)
console.log(e.isAxiosError)
})
网友评论