diff --git a/__tests__/axios.spec.ts b/__tests__/axios.spec.ts
new file mode 100644
index 0000000..b12285c
--- /dev/null
+++ b/__tests__/axios.spec.ts
@@ -0,0 +1,25 @@
+import { createAxios } from '../src/axios';
+
+describe('请求非 JSON 数据', () => {
+ it('XML 数据', async () => {
+ const axios = createAxios();
+ const res = await axios.get('https://httpbin.org/robots.txt');
+ expect(res.data).toBe('User-agent: *\nDisallow: /deny\n');
+ });
+});
+
+describe('请求 JSON 数据', () => {
+ it('普通数据', async () => {
+ const axios = createAxios();
+ const res = await axios.get('https://httpbin.org/json');
+ const expected = JSON.parse('{"slideshow":{"author":"Yours Truly","date":"date of publication","slides":[{"title":"Wake up to WonderWidgets!","type":"all"},{"items":["Why WonderWidgets are great","Who buys WonderWidgets"],"title":"Overview","type":"all"}],"title":"Sample Slide Show"}}');
+ expect(res.data).toMatchObject(expected);
+ });
+
+ it('带 bigint', async () => {
+ const axios = createAxios();
+ const obj = { id: 9223372036854775808n };
+ const res = await axios.post('https://httpbin.org/anything', obj);
+ expect(res.data.json).toMatchObject(obj);
+ });
+});
diff --git a/package.json b/package.json
index 5e92e30..6695856 100644
--- a/package.json
+++ b/package.json
@@ -59,6 +59,7 @@
},
"dependencies": {
"axios": "^1.6.2",
+ "is-plain-object": "^5.0.0",
"js-base64": "^3.7.5",
"json-bigint": "^1.0.0",
"mitt": "^3.0.1",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f7951dc..aabc4a7 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -4,6 +4,9 @@ dependencies:
axios:
specifier: ^1.6.2
version: 1.6.2
+ is-plain-object:
+ specifier: ^5.0.0
+ version: 5.0.0
js-base64:
specifier: ^3.7.5
version: 3.7.5
@@ -3239,6 +3242,11 @@ packages:
engines: {node: '>=8'}
dev: true
+ /is-plain-object@5.0.0:
+ resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
/is-stream@2.0.1:
resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
engines: {node: '>=8'}
diff --git a/src/axios.ts b/src/axios.ts
index 2b8c0c1..27d7014 100644
--- a/src/axios.ts
+++ b/src/axios.ts
@@ -1,4 +1,5 @@
-import axios, { AxiosResponse, CreateAxiosDefaults } from 'axios';
+import axios, { AxiosResponse, CreateAxiosDefaults, InternalAxiosRequestConfig } from 'axios';
+import { isPlainObject } from 'is-plain-object';
import jsonBigint from 'json-bigint';
import { FanbookApiError } from './error';
@@ -7,21 +8,40 @@ export const bigintJsonParser = jsonBigint({
useNativeBigInt: true,
});
+/** 测试 Content-Type 是否为 json 的正则表达式。 */
+const isJsonContentType = /^application\/(.*\+)?json$/;
/**
- * 转换请求响应。
- * @param data 响应体
- * @returns 转换后的响应体
+ * 解析请求 bigint。
+ * @param req 请求数据
+ * @returns 转换后的请求数据
*/
-function parseResponseBigint(data: unknown) {
- console.log(data);
- if (typeof data === 'string') {
- try { // 能转 JSON 就转
- return bigintJsonParser.parse(data);
- } catch { // 不能转原样返回
- return data;
- }
+function requestBigintInterceptor(req: InternalAxiosRequestConfig) {
+ const type = req.headers['content-type'];
+ if (
+ (typeof type === 'string' && isJsonContentType.test(type))
+ || isPlainObject(req.data)
+ ) { // 是 JSON
+ req.data = bigintJsonParser.stringify(req.data);
+ req.headers['content-type'] ??= 'application/json';
}
- return data;
+ return req;
+}
+/**
+ * 解析响应 bigint。
+ * @param res 响应数据
+ * @returns 转换后的响应数据
+ */
+function responseBigintInterceptor(res: AxiosResponse) {
+ // 非 json 不处理
+ const type = res.headers['content-type'];
+ if (typeof type !== 'string') return res;
+ if (!isJsonContentType.test(type)) return res;
+
+ // 带 bigint 解析 json,不行就算了
+ try {
+ res.data = bigintJsonParser.parse(res.data);
+ } catch {}
+ return res;
}
/**
@@ -30,13 +50,17 @@ function parseResponseBigint(data: unknown) {
* @returns Axios 实例
*/
export function createAxios(options?: CreateAxiosDefaults) {
- let pipes = options?.transformResponse;
- // 将 pipes 转数组,然后浅拷贝,保证后续修改 options 不会影响到 pipes
- if (Array.isArray(pipes)) pipes = [...pipes];
- else pipes = pipes ? [pipes] : []; // pipes 可能为 undefined
-
- pipes.unshift(parseResponseBigint);
- return axios.create(options);
+ const inst = axios.create({
+ ...options,
+ // 由于 axios 自动解析 json 导致 bigint 精度丢失,要阻止它的 json 解析
+ // 如果用户已经给定 pipes,它会阻止 json 解析
+ // 如果用户没有给定 pipes,要用 `(x) => x` 阻止 json 解析
+ transformRequest: options?.transformRequest ?? ((x) => x),
+ transformResponse: options?.transformResponse ?? ((x) => x),
+ });
+ inst.interceptors.request.use(requestBigintInterceptor);
+ inst.interceptors.response.use(responseBigintInterceptor);
+ return inst;
}
/**