导航菜单

  • 0.api
  • 0.Async
  • 0.module
  • 1.ES2015
  • 2.Promise
  • 3.Node
  • 4.NodeInstall
  • 5.REPL
  • 6.NodeCore
  • 7.module&NPM
  • 8.Encoding
  • 9.Buffer
  • 10.fs
  • 11.Stream-1
  • 11.Stream-2
  • 11.Stream-3
  • 11.Stream-4
  • 12-Network-2
  • 12.NetWork-3
  • 12.Network-1
  • 13.tcp
  • 14.http-1
  • 14.http-2
  • 15.compress
  • 16.crypto
  • 17.process
  • 18.yargs
  • 19.cache
  • 20.action
  • 21.https
  • 22.cookie
  • 23.session
  • 24.express-1
  • 24.express-2
  • 24.express-3
  • 24.express-4
  • 25.koa-1
  • 26.webpack-1-basic
  • 26.webpack-2-optimize
  • 26.webpack-3-file
  • 26.webpack-4.tapable
  • 26.webpack-5-AST
  • 26.webpack-6-sources
  • 26.webpack-7-loader
  • 26.webpack-8-plugin
  • 26.webpack-9-hand
  • 26.webpack-10-prepare
  • 28.redux
  • 28.redux-jwt-back
  • 28.redux-jwt-front
  • 29.mongodb-1
  • 29.mongodb-2
  • 29.mongodb-3
  • 29.mongodb-4
  • 29.mongodb-5
  • 29.mongodb-6
  • 30.cms-1-mysql
  • 30.cms-2-mysql
  • 30.cms-3-mysql
  • 30.cms-4-nunjucks
  • 30.cms-5-mock
  • 30.cms-6-egg
  • 30.cms-7-api
  • 30.cms-8-roadhog
  • 30.cms-9-yaml
  • 30.cms-10-umi
  • 30.cms-12-dva
  • 30.cms-13-dva-ant
  • 30.cms-14-front
  • 30.cms-15-deploy
  • 31.dva
  • 31.cms-13-dva-antdesign
  • 33.redis
  • 34.unittest
  • 35.jwt
  • 36.websocket-1
  • 36.websocket-2
  • 38.chat-api-1
  • 38.chat-api-2
  • 38.chat-3
  • 38.chat-api-3
  • 38.chat
  • 38.chat2
  • 38.chat2
  • 39.crawl-0
  • 39.crawl-1
  • 39.crawl-2
  • 40.deploy
  • 41.safe
  • 42.test
  • 43.nginx
  • 44.enzyme
  • 45.docker
  • 46.elastic
  • 47.oauth
  • 48.wxpay
  • index
  • 52.UML
  • 53.design
  • index
  • 54.linux
  • 57.ts
  • 56.react-ssr
  • 58.ts_react
  • 59.ketang
  • 59.ketang2
  • 61.1.devops-linux
  • 61.2.devops-vi
  • 61.3.devops-user
  • 61.4.devops-auth
  • 61.5.devops-shell
  • 61.6.devops-install
  • 61.7.devops-system
  • 61.8.devops-service
  • 61.9.devops-network
  • 61.10.devops-nginx
  • 61.11.devops-docker
  • 61.12.devops-jekins
  • 61.13.devops-groovy
  • 61.14.devops-php
  • 61.15.devops-java
  • 61.16.devops-node
  • 61.17.devops-k8s
  • 62.1.react-basic
  • 62.2.react-state
  • 62.3.react-high
  • 62.4.react-optimize
  • 62.5.react-hooks
  • 62.6.react-immutable
  • 62.7.react-mobx
  • 62.8.react-source
  • 63.1.redux
  • 63.2.redux-middleware
  • 63.3.redux-hooks
  • 63.4.redux-saga
  • 63.5.redux-saga-hand
  • 64.1.router
  • 64.2.router-connected
  • 65.1.typescript
  • 65.2.typescript
  • 65.3.typescript
  • 65.4.antd
  • 65.4.definition
  • 66-1.vue-base
  • 66-2.vue-component
  • 66-3.vue-cli3.0
  • 66-4.$message组件
  • 66-5.Form组件
  • 66-6.tree
  • 66-7.vue-router-apply
  • 66-8.axios-apply
  • 66-9.vuex-apply
  • 66-10.jwt-vue
  • 66-11.vue-ssr
  • 66-12.nuxt-apply
  • 66-13.pwa
  • 66-14.vue单元测试
  • 66-15.权限校验
  • 67-1-network
  • 68-2-wireshark
  • 7.npm2
  • 69-hooks
  • 70-deploy
  • 71-hmr
  • 72.deploy
  • 73.import
  • 74.mobile
  • 75.webpack-1.文件分析
  • 75.webpack-2.loader
  • 75.webpack-3.源码流程
  • 75.webpack-4.tapable
  • 75.webpack-5.prepare
  • 75.webpack-6.resolve
  • 75.webpack-7.loader
  • 75.webpack-8.module
  • 75.webpack-9.chunk
  • 75.webpack-10.asset
  • 75.webpack-11.实现
  • 76.react_optimize
  • 77.ts_ketang_back
  • 77.ts_ketang_front
  • 78.vue-domdiff
  • 79.grammar
  • 80.tree
  • 81.axios
  • 82.1.react
  • 82.2.react-high
  • 82.3.react-router
  • 82.4.redux
  • 82.5.redux_middleware
  • 82.6.connected
  • 82.7.saga
  • 82.8.dva
  • 82.8.dva-source
  • 82.9.roadhog
  • 82.10.umi
  • 82.11.antdesign
  • 82.12.ketang-front
  • 82.12.ketang-back
  • 83.upload
  • 84.graphql
  • 85.antpro
  • 86.1.uml
  • 86.2.design
  • 87.postcss
  • 88.react16-1
  • 89.nextjs
  • 90.react-test
  • 91.react-ts
  • 92.rbac
  • 93.tsnode
  • 94.1.JavaScript
  • 94.2.JavaScript
  • 94.3.MODULE
  • 94.4.EventLoop
  • 94.5.文件上传
  • 94.6.https
  • 94.7. nginx
  • 95.1. react
  • 95.2.react
  • 96.1.react16
  • 96.2.fiber
  • 96.3.fiber
  • 97.serverless
  • 98.websocket
  • 100.1.react-basic
  • 101.1.monitor
  • 101.2.monitor
  • 102.java
  • 103.1.webpack-usage
  • 103.2.webpack-bundle
  • 103.3.webpack-ast
  • 103.4.webpack-flow
  • 103.5.webpack-loader
  • 103.6.webpack-tapable
  • 103.7.webpack-plugin
  • 103.8.webpack-optimize1
  • 103.9.webpack-optimize2
  • 103.10.webpack-hand
  • 103.11.webpack-hmr
  • 103.11.webpack5
  • 103.13.splitChunks
  • 103.14.webpack-sourcemap
  • 103.15.webpack-compiler1
  • 103.15.webpack-compiler2
  • 103.16.rollup.1
  • 103.16.rollup.2
  • 103.16.rollup.3
  • 103.16.vite.basic
  • 103.16.vite.source
  • 103.16.vite.plugin
  • 103.16.vite.1
  • 103.16.vite.2
  • 103.17.polyfill
  • 104.1.binary
  • 104.2.binary
  • 105.skeleton
  • 106.1.react
  • 106.2.react_hooks
  • 106.3.react_router
  • 106.4.redux
  • 106.5.redux_middleware
  • 106.6.connected-react-router
  • 106.6.redux-first-history
  • 106.7.redux-saga
  • 106.8.dva
  • 106.9.umi
  • 106.10.ketang
  • 106.11.antdesign
  • 106.12.antpro
  • 106.13.router-6
  • 106.14.ssr
  • 106.15.nextjs
  • 106.16.1.cms
  • 106.16.2.cms
  • 106.16.3.cms
  • 106.16.4.cms
  • 106.16.mobx
  • 106.17.fomily
  • 107.fiber
  • 108.http
  • 109.1.webpack_usage
  • 109.2.webpack_source
  • 109.3.dll
  • 110.nest.js
  • 111.xstate
  • 112.Form
  • 113.redux-saga
  • 114.react+typescript
  • 115.immer
  • 116.pro5
  • 117.css-loader
  • 118.1.umi-core
  • 119.2.module-federation
  • 119.1.module-federation
  • 120.create-react-app
  • 121.react-scripts
  • 122.react-optimize
  • 123.jsx-runtime
  • 124.next.js
  • 125.1.linux
  • 125.2.linux-vi
  • 125.3.linux-user
  • 125.4.linux-auth
  • 125.5.linux-shell
  • 125.6.linux-install
  • 125.7.linux-system
  • 125.8.linux-service
  • 125.9.linux-network
  • 125.10.nginx
  • 125.11.docker
  • 125.12.ci
  • 125.13.k8s
  • 125.14.k8s
  • 125.15.k8s
  • 125.16.k8s
  • 126.11.react-1
  • 126.12.react-2
  • 126.12.react-3
  • 126.12.react-4
  • 126.12.react-5
  • 126.12.react-6
  • 126.12.react-7
  • 126.12.react-8
  • 127.frontend
  • 128.rollup
  • 129.px2rem-loader
  • 130.health
  • 131.hooks
  • 132.keepalive
  • 133.vue-cli
  • 134.react18
  • 134.2.react18
  • 134.3.react18
  • 135.function
  • 136.toolkit
  • 137.lerna
  • 138.create-vite
  • 139.cli
  • 140.antd
  • 141.react-dnd
  • 142.1.link
  • 143.1.gulp
  • 143.2.stream
  • 143.3.gulp
  • 144.1.closure
  • 144.2.v8
  • 144.3.gc
  • 145.react-router-v6
  • 146.browser
  • 147.lighthouse
  • 148.1.basic
  • 148.2.basic
  • 148.3.basic
  • 148.4.basic
  • 148.5.basic
  • 149.1.vite
  • 149.2.vite
  • 149.3.vite
  • 149.4.vite
  • 150.react-window
  • 151.react-query
  • 152.useRequest
  • 153.transition
  • 154.emotion
  • 155.1.formily
  • 155.2.formily
  • 155.3.formily
  • 155.3.1.mobx.usage
  • 155.3.2.mobx.source
  • 156.vue-loader
  • 103.11.mf
  • 157.1.react18
  • 158.umi4
  • 159.rxjs
  • 159.rxjs2
  • 160.bff
  • 161.zustand
  • 162.vscode
  • 163.emp
  • 164.cors
  • 1.生成项目
  • 2. get请求
    • 2.1 src\index.tsx
    • 2.2 axios\index.tsx
    • 2.2 axios\Axios.tsx
    • 2.3 axios\types.tsx
  • 3. POST请求
    • 3.1 src\index.tsx
    • 3.2 axios\types.tsx
    • 3.3 axios\Axios.tsx
  • 4. 错误处理
    • 4.1 axios\Axios.tsx
    • 4.2 src\index.tsx
      • 4.2.1 网络错误
      • 4.2.2 超时
      • 4.2.3 错误状态码
  • 5. 拦截器功能
    • 5.1 src\index.tsx
    • 5.2 src\axios\types.tsx
    • 5.3 AxiosInterceptorManager.ts
    • 5.3 src\axios\Axios.tsx
  • 6. 合并配置
    • 6.1 axios\types.tsx
    • 6.2 Axios.tsx
  • 7.转换请求与响应
    • 7.1 axios\types.tsx
    • 7.2 axios\Axios.tsx
  • 8.任务取消
    • 8.1 src\index.tsx
    • 8.2 axios\types.tsx
    • 8.3 axios\cancel.tsx
    • 8.4 axios\index.tsx
    • 8.5 axios\Axios.tsx
  • 9.后端接口

1.生成项目 #

npx create-react-app zaxios --typescript
cd create-react-app
yarn add axios @types/axios qs @types/qs parse-headers
yarn add express body-parser
yarn start

2. get请求 #

  • axios

1.axiosresponse

2.axiosresponse.png

2.1 src\index.tsx #

src\index.tsx

import axios, { AxiosResponse } from './axios';
const baseURL = 'http://localhost:8080';
export interface User {
    username: string;
    password: string;
}
let user: User = {
    username: 'zhufeng',
    password: '123456'
};
axios({
    method: 'get',
    url: baseURL + '/get',
    params: user
}).then((response: AxiosResponse) => {
    console.log(response);
    return response.data;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    console.log(error);
});

2.2 axios\index.tsx #

src\axios\index.tsx

import Axios from './Axios';
import { AxiosInstance } from './types';
function createInstance(): AxiosInstance {
    let context = new Axios();
    let instance = Axios.prototype.request.bind(context);
    instance = Object.assign(instance, Axios.prototype, context);
    return instance as AxiosInstance;
}
var axios = createInstance();
export default axios;
export * from './types';

2.2 axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import qs from 'qs';
import parse from 'parse-headers';
class Axios {
    request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.dispatchRequest(config);
    }
    dispatchRequest(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return new Promise((resolve, reject) => {
            let { method = 'get', url, params } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        resolve(response);
                    } else {
                        reject('请求失败');
                    }
                }
            }
            request.send();
        });
    }
}
export default Axios;

2.3 axios\types.tsx #

src\axios\types.tsx

export type Methods = 'GET' | 'get' | 'POST' | 'post' | 'PUT' | 'put' | 'DELETE' | 'delete';
export interface AxiosInstance {
    <T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>
}
export interface AxiosRequestConfig {
    url: string;
    method?: Methods;
    params?: Record<string, any>
}

export interface AxiosResponse<T = any> {
    data: T;
    status: number;
    statusText: string;
    headers: any;
    config: AxiosRequestConfig;
    request?: any;
}

3. POST请求 #

3.1 src\index.tsx #

import axios, { AxiosResponse } from './axios';
const baseURL = 'http://localhost:8080';
export interface User {
    username: string;
    password: string;
}
let user: User = {
    username: 'zhufeng',
    password: '123456'
};
axios({
+    method: 'post',
+    url: baseURL + '/post',
+    headers: { 'Content-Type': 'application/json' },
+    data: user,
}).then((response: AxiosResponse) => {
    console.log(response);
    return response.data;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    console.log(error);
});

3.2 axios\types.tsx #

src\axios\types.tsx

export type Methods = 'GET' | 'get' | 'POST' | 'post' | 'PUT' | 'put' | 'DELETE' | 'delete';
export interface AxiosInstance {
    <T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>
}
export interface AxiosRequestConfig {
    url: string;
    method?: Methods;
    params?: Record<string, any>;
+    data?: Record<string, any>;
+    headers?: Record<string, any>;
}

export interface AxiosResponse<T = any> {
    data: T;
    status: number;
    statusText: string;
    headers: any;
    config: AxiosRequestConfig;
    request?: any;
}

3.3 axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import qs from 'qs';
import parse from 'parse-headers';
class Axios {
    request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.dispatchRequest(config);
    }
    dispatchRequest(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return new Promise((resolve, reject) => {
+           let { method = 'get', url, params, headers, data } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        resolve(response);
                    } else {
                        reject('请求失败');
                    }
                }
            }
+           if (headers) {
+               for (let key in headers) {
+                   request.setRequestHeader(key, headers[key]);
+               }
+           }
+           let body: string | null = null;
+           if (data && typeof data == 'object') {
+               body = JSON.stringify(data);
+           }
+           request.send(body);
        });
    }
}
export default Axios;

4. 错误处理 #

  • 网络异常
  • 超时异常
  • 错误状态码

4.1 axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import qs from 'qs';
import parse from 'parse-headers';
class Axios {
    request(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return this.dispatchRequest(config);
    }
    dispatchRequest(config: AxiosRequestConfig): Promise<AxiosResponse> {
        return new Promise((resolve, reject) => {
+           let { method = 'get', url, params, headers, data, timeout } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
+               if (request.readyState === 4 && request.status !== 0) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        resolve(response);
                    } else {
+                        reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
            if (headers) {
                for (let key in headers) {
                    request.setRequestHeader(key, headers[key]);
                }
            }
            let body: string | null = null;
            if (data && typeof data == 'object') {
                body = JSON.stringify(data);
            }
+           request.onerror = () => { //网络异常
+               reject(new Error('Network Error'));
+           }
+           if (timeout) {
+               request.timeout = timeout;
+               request.ontimeout = () => { //超时异常
+                   reject(new Error(`timeout of ${timeout}ms exceeded`));
+               }
+           }
            request.send(body);
        });
    }
}
export default Axios;

4.2 src\index.tsx #

4.2.1 网络错误 #

+setTimeout(() => {
    axios({
        method: 'post',
        url: baseURL + '/post',
        headers: { 'Content-Type': 'application/json' },
        data: user,
    }).then((response: AxiosResponse) => {
        console.log(response);
        return response.data;
    }).then((data: User) => {
        console.log(data);
    }).catch(function (error: any) {
        console.log(error);
    });
+}, 5000);

4.2.2 超时 #

axios({
    method: 'post',
+    url: baseURL + '/post_timeout?timeout=3000',
    headers: { 'Content-Type': 'application/json' },
+    timeout: 1000,
    data: user,
}).then((response: AxiosResponse) => {
    console.log(response);
    return response.data;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    console.log(error);
});

4.2.3 错误状态码 #

axios({
    method: 'post',
+    url: baseURL + '/post_status?code=300',
    headers: { 'Content-Type': 'application/json' },
    timeout: 1000,
    data: user,
}).then((response: AxiosResponse) => {
    console.log(response);
    return response.data;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    console.log(error);
});

5. 拦截器功能 #

5.1 src\index.tsx #

src\index.tsx

import axios from './axios'
import { AxiosResponse, AxiosRequestConfig } from './axios';
const baseURL = 'http://localhost:8080';
interface User {
    username: string;
    password: string;
}
let user: User = {
    username: 'zhufeng',
    password: '123456'
};
+console.time('cost');
+axios.interceptors.request.use((config: AxiosRequestConfig) => {
+    console.timeEnd('cost');
+    config.headers!.name += '1';
+    return config;
+    //return Promise.reject('在1处失败了!');
+})
+let request_interceptor = axios.interceptors.request.use((config: AxiosRequestConfig) => {
+    config.headers!.name += '2';
+    return config;
+})
+axios.interceptors.request.use((config: AxiosRequestConfig) => {
+    return new Promise<AxiosRequestConfig>(function (resolve) {
+        setTimeout(function () {
+            config.headers!.name += '3';
+            resolve(config);
+        }, 3000);
+    });
+})
+axios.interceptors.request.eject(request_interceptor);
+axios.interceptors.response.use((response: AxiosResponse) => {
+    response.data.username += '1'
+    return response;
+})
+let response_interceptor = axios.interceptors.response.use((response: AxiosResponse) => {
+    response.data.username += '2'
+    return response;
+})
+axios.interceptors.response.use((response: AxiosResponse) => {
+    response.data.username += '3';
+    return response;
+    //return Promise.reject('失败了');
+})

+axios.interceptors.response.eject(response_interceptor);
axios<User>({
    method: 'post',
+    url: baseURL + '/post',
+    headers: { 'Content-Type': 'application/json', name: 'name' },
    data: user,
+}).then((response: AxiosRequestConfig | AxiosResponse<User>) => {
    console.log(response);
    console.timeEnd('cost');
+    return response.data as User;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    console.log('error', error);
});

5.2 src\axios\types.tsx #

src\axios\types.tsx

+import AxiosInterceptorManager from './AxiosInterceptorManager';
export type Methods = 'GET' | 'get' | 'POST' | 'post' | 'PUT' | 'put' | 'DELETE' | 'delete';
export interface AxiosInstance {
    <T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>;
+    interceptors: {
+        request: AxiosInterceptorManager<AxiosRequestConfig>;
+        response: AxiosInterceptorManager<AxiosResponse>;
+    };
}
export interface AxiosRequestConfig {
    url: string;
    method?: Methods;
    params?: Record<string, any>;
    data?: Record<string, any>;
    headers?: Record<string, any>;
    timeout?: number;
}

export interface AxiosResponse<T = any> {
    data: T;
    status: number;
    statusText: string;
    headers: any;
    config: AxiosRequestConfig;
    request?: any;
}

5.3 AxiosInterceptorManager.ts #

src\axios\AxiosInterceptorManager.ts

export interface OnFulfilledFn<V> {
    (value: V): V | Promise<V>
}

export interface OnRejectedFn {
    (error: any): any
}
export interface Interceptor<T = any> {
    onFulfilled: OnFulfilledFn<T>
    onRejected?: OnRejectedFn
}

export default class InterceptorManager<V> {
    private interceptors: Array<Interceptor<V> | null> = []
    use(onFulfilled: OnFulfilledFn<V>, onRejected?: OnRejectedFn): number {
        this.interceptors.push({
            onFulfilled,
            onRejected
        })
        return this.interceptors.length - 1;
    }
    eject(id: number): void {
        if (this.interceptors[id]) {
            this.interceptors[id] = null;
        }
    }
}

5.3 src\axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
+import AxiosInterceptorManager, { Interceptor } from './AxiosInterceptorManager';
import qs from 'qs';
import parse from 'parse-headers';
+interface Interceptors {
+    request: AxiosInterceptorManager<AxiosRequestConfig>;
+    response: AxiosInterceptorManager<AxiosResponse>;
+}
class Axios {
+    public interceptors: Interceptors = {
+        request: new AxiosInterceptorManager<AxiosRequestConfig>(),
+        response: new AxiosInterceptorManager<AxiosResponse>()
+    }
+    request<T>(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
-        return this.dispatchRequest(config);
+        const chain: Interceptor[] = [
+            {
+                onFulfilled: this.dispatchRequest,
+                onRejected: undefined
+            }
+        ]
+        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
+            interceptor && chain.unshift(interceptor);
+        })

+        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse<T>> | null) => {
+            interceptor && chain.push(interceptor)
+        })
+        let promise: Promise<AxiosRequestConfig | AxiosResponse<T>> = Promise.resolve(config);

+        while (chain.length) {
+            const { onFulfilled, onRejected } = chain.shift()!;
+            promise = promise.then(onFulfilled, onRejected);
+        }
+        return promise;
+    }
+    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
+        return new Promise<AxiosResponse<T>>((resolve, reject) => {
            let { method = 'get', url, params, headers, data, timeout } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4 && request.status !== 0) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        resolve(response);
                    } else {
                        reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
            if (headers) {
                for (let key in headers) {
                    request.setRequestHeader(key, headers[key]);
                }
            }
            let body: string | null = null;
            if (data && typeof data == 'object') {
                body = JSON.stringify(data);
            }
            request.onerror = () => { //网络异常
                reject(new Error('Network Error'));
            }
            if (timeout) {
                request.timeout = timeout;
                request.ontimeout = () => { //超时异常
                    reject(new Error(`timeout of ${timeout}ms exceeded`));
                }
            }
            request.send(body);
        });
    }
}
export default Axios;

6. 合并配置 #

6.1 axios\types.tsx #

src\axios\types.tsx

export interface AxiosRequestConfig extends Record<string, any> {
+   url?: string;
    method?: Methods;
    params?: Record<string, any>;
    data?: Record<string, any>;
    headers?: Record<string, any>;
    timeout?: number;
}

6.2 Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import AxiosInterceptorManager, { Interceptor } from './AxiosInterceptorManager';
import qs from 'qs';
import parse from 'parse-headers';
interface Interceptors {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
}
+let defaults: AxiosRequestConfig = {
+    method: 'get',
+    timeout: 0,
+    headers: {
+        common: {
+            accept: 'application/json'
+        }
+    }
+}
+let getStyleMethods = ['get', 'head', 'delete', 'options'];
+getStyleMethods.forEach((method: string) => {
+    defaults.headers![method] = {};
+});

+let postStyleMethods = ['put', 'post', 'patch'];
+postStyleMethods.forEach((method: string) => {
+    defaults.headers![method] = {};
+});
+let allMethods = [...getStyleMethods, ...postStyleMethods];
class Axios {
+    public defaults: AxiosRequestConfig = defaults;
    public interceptors: Interceptors = {
        request: new AxiosInterceptorManager<AxiosRequestConfig>(),
        response: new AxiosInterceptorManager<AxiosResponse>()
    }
    request<T>(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
        //return this.dispatchRequest(config);
+       config.headers = Object.assign(this.defaults.headers, config.headers);
        const chain: Interceptor[] = [
            {
                onFulfilled: this.dispatchRequest,
                onRejected: undefined
            }
        ]
        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
            interceptor && chain.unshift(interceptor);
        })

        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse<T>> | null) => {
            interceptor && chain.push(interceptor)
        })
        let promise: Promise<AxiosRequestConfig | AxiosResponse<T>> = Promise.resolve(config);

        while (chain.length) {
            const { onFulfilled, onRejected } = chain.shift()!;
            promise = promise.then(onFulfilled, onRejected);
        }
        return promise;
    }
    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
        return new Promise<AxiosResponse<T>>((resolve, reject) => {
            let { method = 'get', url, params, headers, data, timeout } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
+               url = url + (url!.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
+            request.open(method, url!, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4 && request.status !== 0) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        resolve(response);
                    } else {
                        reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
+            if (headers) {
+                for (let key in headers) {
+                    if (key === 'common' || allMethods.includes(key)) {
+                        for (let key2 in headers[key]) {
+                            request.setRequestHeader(key2, headers[key][key2]);
+                        }
+                    } else {
+                        request.setRequestHeader(key, headers[key]);
+                    }
+                }
+            }
            let body: string | null = null;
            if (data && typeof data == 'object') {
                body = JSON.stringify(data);
            }
            request.onerror = () => { //网络异常
                reject(new Error('Network Error'));
            }
            if (timeout) {
                request.timeout = timeout;
                request.ontimeout = () => { //超时异常
                    reject(new Error(`timeout of ${timeout}ms exceeded`));
                }
            }
            request.send(body);
        });
    }
}
export default Axios;

7.转换请求与响应 #

7.1 axios\types.tsx #

src\axios\types.tsx

export interface AxiosRequestConfig extends Record<string, any> {
    url?: string;
    method?: Methods;
    params?: Record<string, any>;
    data?: Record<string, any>;
    headers?: Record<string, any>;
    timeout?: number;
+    transformRequest?: (data: Record<string, any>, headers: Record<string, any>) => any;
+    transformResponse?: (data: any) => any;
}

7.2 axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import AxiosInterceptorManager, { Interceptor } from './AxiosInterceptorManager';
import qs from 'qs';
import parse from 'parse-headers';
interface Interceptors {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
}
let defaults: AxiosRequestConfig = {
    method: 'get',
    timeout: 0,
    headers: {
        common: {
            accept: 'application/json'
        }
    },
+    transformRequest: function (data: Record<string, any>, headers: Record<string, any>) {
+        headers['content-type'] = 'application/x-www-form-urlencoded';
+        return qs.stringify(data);
+    },
+    transformResponse(data: any) {
+        if (typeof data == 'string')
+            data = JSON.parse(data);
+        return data;
+    },
}
let getStyleMethods = ['get', 'head', 'delete', 'options'];
getStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {};
});

let postStyleMethods = ['put', 'post', 'patch'];
postStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {};
});
let allMethods = [...getStyleMethods, ...postStyleMethods];
class Axios {
    public defaults: AxiosRequestConfig = defaults;
    public interceptors: Interceptors = {
        request: new AxiosInterceptorManager<AxiosRequestConfig>(),
        response: new AxiosInterceptorManager<AxiosResponse>()
    }
    request<T>(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
        //return this.dispatchRequest(config);
+        if (config.transformRequest && config.data)
+            config.data = config.transformRequest(config.data, config.headers = {});
        config.headers = Object.assign(this.defaults.headers, config.headers);
        const chain: Interceptor[] = [
            {
                onFulfilled: this.dispatchRequest,
                onRejected: undefined
            }
        ]
        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
            interceptor && chain.unshift(interceptor);
        })

        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse<T>> | null) => {
            interceptor && chain.push(interceptor)
        })
        let promise: Promise<AxiosRequestConfig | AxiosResponse<T>> = Promise.resolve(config);

        while (chain.length) {
            const { onFulfilled, onRejected } = chain.shift()!;
            promise = promise.then(onFulfilled, onRejected);
        }
        return promise;
    }
    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
        return new Promise<AxiosResponse<T>>((resolve, reject) => {
            let { method = 'get', url, params, headers, data, timeout } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url!.indexOf('?') == -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url!, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4 && request.status !== 0) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
+                        if (config.transformResponse) {
+                            response.data = config.transformResponse(response.data);
+                        }
                        resolve(response);
                    } else {
                        reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
            if (headers) {
                for (let key in headers) {
                    if (key === 'common' || allMethods.includes(key)) {
                        for (let key2 in headers[key]) {
                            request.setRequestHeader(key2, headers[key][key2]);
                        }
                    } else {
                        request.setRequestHeader(key, headers[key]);
                    }
                }
            }
            let body: string | null = null;
            if (data && typeof data == 'object') {
                body = JSON.stringify(data);
            }
            request.onerror = () => { //网络异常
                reject(new Error('Network Error'));
            }
            if (timeout) {
                request.timeout = timeout;
                request.ontimeout = () => { //超时异常
                    reject(new Error(`timeout of ${timeout}ms exceeded`));
                }
            }
            request.send(body);
        });
    }
}
export default Axios;

8.任务取消 #

8.1 src\index.tsx #

src\index.tsx

import axios from './axios'
import { AxiosResponse, AxiosRequestConfig } from './axios';
interface User {
    username: string;
    password: string;
}
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios({
    method: 'post',
    baseURL: 'http://localhost:8080',
    url: '/post_timeout?timeout=2000',
    timeout: 3000,
    cancelToken: source.token
}).then((response: AxiosRequestConfig | AxiosResponse<User>) => {
    console.log(response);
    return response.data as User;
}).then((data: User) => {
    console.log(data);
}).catch(function (error: any) {
    if (axios.isCancel(error)) {
        console.log('请求取消', error);
    } else {
        console.log('error', error);
    }
});
source.cancel('用户取消请求');

8.2 axios\types.tsx #

src\axios\types.tsx

export interface AxiosInstance {
    <T = any>(config: AxiosRequestConfig): Promise<AxiosResponse<T>>;
    interceptors: {
        request: AxiosInterceptorManager<AxiosRequestConfig>;
        response: AxiosInterceptorManager<AxiosResponse>;
    };
+    CancelToken: any;
+    isCancel: any
}

8.3 axios\cancel.tsx #

src\axios\cancel.tsx

export class Cancel {
    constructor(public reason: string) { }
}
export function isCancel(error: any) {
    return error instanceof Cancel;
}
export class CancelToken {
    public resolve: any;
    source() {
        return {
            token: new Promise((resolve) => {
                this.resolve = resolve;
            }),
            cancel: (reason: string) => {
                this.resolve(new Cancel(reason));
            }
        }
    }
}

8.4 axios\index.tsx #

src\axios\index.tsx

import Axios from './Axios';
import { AxiosInstance } from './types';
+import { CancelToken, isCancel } from './cancel';
function createInstance(): AxiosInstance {
    let context = new Axios();
    let instance = Axios.prototype.request.bind(context);
    instance = Object.assign(instance, Axios.prototype, context);
    return instance as AxiosInstance;
}
var axios = createInstance();
+axios.CancelToken = new CancelToken();
+axios.isCancel = isCancel;
export default axios;
export * from './types';

8.5 axios\Axios.tsx #

src\axios\Axios.tsx

import { AxiosRequestConfig, AxiosResponse } from './types';
import AxiosInterceptorManager, { Interceptor } from './AxiosInterceptorManager';
import qs from 'qs';
import parse from 'parse-headers';
interface Interceptors {
    request: AxiosInterceptorManager<AxiosRequestConfig>;
    response: AxiosInterceptorManager<AxiosResponse>;
}
let defaults: AxiosRequestConfig = {
    method: 'get',
    timeout: 0,
    headers: {
        common: {
            accept: 'application/json'
        }
    },
    transformRequest: function (data: Record<string, any>, headers: Record<string, any>) {
        headers['content-type'] = 'application/x-www-form-urlencoded';
        return qs.stringify(data);
    },
    transformResponse(data: any) {
        if (typeof data == 'string')
            data = JSON.parse(data);
        return data;
    },
}
let getStyleMethods = ['get', 'head', 'delete', 'options'];
getStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {};
});

let postStyleMethods = ['put', 'post', 'patch'];
postStyleMethods.forEach((method: string) => {
    defaults.headers![method] = {};
});
let allMethods = [...getStyleMethods, ...postStyleMethods];
class Axios {
    public defaults: AxiosRequestConfig = defaults;
    public interceptors: Interceptors = {
        request: new AxiosInterceptorManager<AxiosRequestConfig>(),
        response: new AxiosInterceptorManager<AxiosResponse>()
    }
    request<T>(config: AxiosRequestConfig): Promise<AxiosRequestConfig | AxiosResponse<T>> {
        //return this.dispatchRequest(config);
        if (config.transformRequest && config.data)
            config.data = config.transformRequest(config.data, config.headers = {});
        config.headers = Object.assign(this.defaults.headers, config.headers);
        config = Object.assign(this.defaults, config);
        if (!config.url!.startsWith('http') && config.baseURL) {
            config.url = config.baseURL + config.url;
        }
        const chain: Interceptor[] = [
            {
                onFulfilled: this.dispatchRequest,
                onRejected: undefined
            }
        ]
        this.interceptors.request.interceptors.forEach((interceptor: Interceptor<AxiosRequestConfig> | null) => {
            interceptor && chain.unshift(interceptor);
        })

        this.interceptors.response.interceptors.forEach((interceptor: Interceptor<AxiosResponse<T>> | null) => {
            interceptor && chain.push(interceptor)
        })
        let promise: Promise<AxiosRequestConfig | AxiosResponse<T>> = Promise.resolve(config);

        while (chain.length) {
            const { onFulfilled, onRejected } = chain.shift()!;
            promise = promise.then(onFulfilled, onRejected);
        }
        return promise;
    }
    dispatchRequest<T>(config: AxiosRequestConfig): Promise<AxiosResponse<T>> {
        return new Promise<AxiosResponse<T>>((resolve, reject) => {
            let { method = 'get', url, params, headers, data, timeout } = config;
            let request: XMLHttpRequest = new XMLHttpRequest();
            if (params) {
                let paramsString = qs.stringify(params);
                url = url + (url!.indexOf('?') === -1 ? '?' : '&') + paramsString;
            }
            request.open(method, url!, true);
            request.responseType = 'json';
            request.onreadystatechange = () => {
                if (request.readyState === 4 && request.status !== 0) {
                    if (request.status >= 200 && request.status < 300) {
                        let response: AxiosResponse = {
                            data: request.response,
                            status: request.status,
                            statusText: request.statusText,
                            headers: parse(request.getAllResponseHeaders()),
                            config: config,
                            request
                        }
                        if (config.transformResponse) {
                            response.data = config.transformResponse(response.data);
                        }
                        resolve(response);
                    } else {
                        reject(new Error(`Request failed with status code ${request.status}`));
                    }
                }
            }
            if (headers) {
                for (let key in headers) {
                    if (key === 'common' || allMethods.includes(key)) {
                        for (let key2 in headers[key]) {
                            request.setRequestHeader(key2, headers[key][key2]);
                        }
                    } else {
                        request.setRequestHeader(key, headers[key]);
                    }
                }
            }
            let body: string | null = null;
            if (data && typeof data == 'object') {
                body = JSON.stringify(data);
            }
            request.onerror = () => { //网络异常
                reject(new Error('Network Error'));
            }
+            if (config.cancelToken) {
+                config.cancelToken.then((reason: string) => {
+                    request.abort();
+                    reject(reason);
+                });
+            }
            if (timeout) {
                request.timeout = timeout;
                request.ontimeout = () => { //超时异常
                    reject(new Error(`timeout of ${timeout}ms exceeded`));
                }
            }
            request.send(body);
        });
    }
}
export default Axios;

9.后端接口 #

let express = require('express');
let bodyParser = require('body-parser');
let app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(function (req, res, next) {
    res.set({
        'Access-Control-Allow-Origin': 'http://localhost:3000',
        'Access-Control-Allow-Credentials': true,
        'Access-Control-Allow-Methods': 'GET,POST, PUT, DELETE, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type,name'
    });
    if (req.method === 'OPTIONS') {
        return res.sendStatus(200);
    }
    next();
});
app.get('/get', function (req, res) {
    res.json(req.query);
});
app.post('/post', function (req, res) {
    res.json(req.body);
});
app.post('/post_timeout', function (req, res) {
    let { timeout } = req.query;
    console.log(req.query);

    if (timeout) {
        timeout = parseInt(timeout);
    } else {
        timeout = 0;
    }
    setTimeout(function () {
        res.json(req.body);
    }, timeout);
});
app.post('/post_status', function (req, res) {
    let { code } = req.query;
    if (code) {
        code = parseInt(code);
    } else {
        code = 200;
    }
    res.statusCode = code;
    res.json(req.body);
});
app.listen(8080);

访问验证

请输入访问令牌

Token不正确,请重新输入