错误处理
网络异常
// ./xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'
// ./src/xhr.ts
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = 'get', headers, responseType } = config
const request = new XMLHttpRequest()
if (responseType) {
request.responseType = responseType
}
request.open(method.toUpperCase(), url, true)
// ...
request.onerror = function handleError() {
reject(new Error('Network Error'))
}
//...
request.send(data)
})
}
处理超时异常
// ./src/type/index.ts
//...
export interface AxiosRequestConfig {
url: string
method?: Method
data?: any
params?: any
headers?: any
responseType?: XMLHttpRequestResponseType
timeout?: number
}
//...
// ./xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'
// ./src/xhr.ts
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = 'get', headers, responseType, timeout } = config
const request = new XMLHttpRequest()
if (responseType) {
request.responseType = responseType
}
if (timeout) {
request.timeout = timeout
}
request.open(method.toUpperCase(), url, true)
//...
request.onerror = function handleError() {
reject(new Error('Network Error'))
}
request.ontimeout = function handleTimeout() {
reject(new Error(`Timeout of ${timeout} ms exceeded`))
}
//...
request.send(data)
})
}
处理非200状态码
// ./xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'
// ./src/xhr.ts
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = 'get', headers, responseType, timeout } = config
const request = new XMLHttpRequest()
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 handleError() {
reject(new Error('Network Error'))
}
request.ontimeout = function handleTimeout() {
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(new Error(`Request failed with status code ${response.status}`))
}
}
})
}
测试
// ./example/error/app.ts
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)
axios({
method: 'get',
url: '/error/timeout',
timeout: 2000
}).then(res => {
console.log(res)
}).catch(e => {
console.log(e.message)
})
// ./example/error/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Error example</title>
</head>
<body>
<script src="/__build__/error.js"></script>
</body>
</html>
// ./examples/server.js
router.get('/error/get', (req, res) => {
if (Math.random() > 0.5) {
res.json({
msg: 'hello world'
})
} else {
res.send(500)
res.end()
}
})
router.get('/error/timeout', (req, res) => {
setTimeout(() => {
res.json({
msg: 'hello world'
})
}, 3000)
})
错误信息的处理
类型定义
// ./src/type/index.ts
export interface AxiosError extends Error {
isAxiosError: boolean
config: AxiosRequestConfig
code?: string | null
request?: any
response?: AxiosResponse
}
类定义
// ./src/helpers/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
) {
const err = new AxiosError(message, config, code, request, response)
return err
}
具体使用
// ./src/xhr.ts
import { AxiosRequestConfig, AxiosPromise, AxiosResponse } from './types'
import { parseHeaders } from './helpers/headers'
import { createError } from './helpers/error'
export default function xhr(config: AxiosRequestConfig): AxiosPromise {
return new Promise((resolve, reject) => {
const { data = null, url, method = 'get', headers, responseType, timeout } = config
const request = new XMLHttpRequest()
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 handleError() {
reject(createError('Network Error', config, null, request))
}
request.ontimeout = function handleTimeout() {
reject(createError(`Timeout of ${timeout} ms exceeded`, config, 'ECONNABORTED', request))
}
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(createError(`Request failed with status code ${response.status}`, config, null, request, response))
}
}
})
}
对外导出类型定义
// ./src/index.ts
import axios from './axios'
export * from './types'
export default axios
测试
// ./example/error/app.ts
import axios, { AxiosError } 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)
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)
})