axios 在项目中的应用

提到 axios 都不陌生,由之前的 XMLHttpRequest -> $.ajax -> Fetch 发展,看来 Promise 是趋势,而项目中也是由 vue-resource 的 this.$http "净化" 成了 axios ,但实际项目中不只是引用 axios 发送请求那么简单,往往还需要进行封装,以下就分享下我们项目中使用 axios 都做了哪些事。

当然不涉及到项目任何隐私,只是纯粹的技术,对,很纯粹。

封装 Service 层

不直接引用 axios 发送请求,而是统一调用 service.js ,在 Service 层里会做很多事。

创建 Service 

  • const service = axios.create({
    timeout: 10000,
    headers: {
    post: {
    'Content-Type': 'application/json;charset=UTF-8',
    },
    },
    emulateJSON: true,
    withCredentials: true,
    });

使用 axios.create 创建一个实例,并且配置一些 JSON 、超时、跨域之类的属性。

国际化

统一使用 service.interceptors.request.use 拦截,并注入国际化标识,比如 request.headers['Accept-Language'] 或者 request.params.lang 等。

规范化后端数据结构

在 service.interceptors.response.use 里统一后端返回结构,比如最终会序列化成:

{
success: Boolean,
code: Number,
message: String,
data: Any,
}

这样好处是说业务层不需要关心各个服务端返回的是否正确,而规范化后端数据时,需要跟各个服务端沟通好返回的字段标识,以方便来转成规范化结构,比如A端认为 code === 200 才是成功,B端认为 code=0 才是成功。

接口容错

对于前端服务而言,我们认为不应该出现报错到控制台,而应该更友好的展示出来,甚至于把错误信息上报到监控中心,在 Service 对外暴露 POST 、GET 方法时,统一使用 try 拦截,如:​​​​​​​

/**
* 重构 service 输出,为了兼容接口异常,导致js崩溃
*/
export default {
async get(...options) {
try {
const res = await service.get(...options);
return res;
} catch (e) {
return Promise.resolve({
success: false,
code: 500,
message: '服务出错,请稍候重试',
});
}
},
async post(...options) {
try {
const res = await service.post(...options);
return res;
} catch (e) {
return Promise.resolve({
success: false,
code: 500,
message: '服务出错,请稍候重试',
});
}
},
};

这样在业务层就可以直接使用数据判断,而不用再 try 或者 .catch 处理,如:

const res = await service.get(uri);

// 绝对是成功
if (res.success) {

} else {
alert(res.message);
}

当然这一伟大的逻辑是 @远高 添加,非常厉害。

统一鉴权

因为有规范化后端数据结构,并且结合 service.interceptors.request.use 拦截,可以很方便的对后端鉴权进行处理,比如:A端的A接口返回失败则自动跳转到登录页。甚至可以统一处理帐户的黑、白名单。

URI 统一化

经过以上处理后整个 Service 整个都纵享丝滑,但项目应用时发现:

  1. 针对某些接口处理事情时比较繁琐,需要不断的向拦截层添加代码
  2. 接口链接更换时,很依赖全局查找

针对以上的痛点,我们添加了 URI 前缀统一化,处理逻辑是添加一层配置层,把 URI 的前缀使用统一字符替换,如:@a/ 、@b/ 这些分别代表不同的接口方,而在使用时可以直接使用这些标识去请求, Service 层会统一处理替换,如:

export const api = {
'@a/': {
url: 'https://www.demo.com/api/a/',
// 自定义统一 headers
headers(service.config) {
return {
};
},

// 自定义统一参数
params(service.config) {
return {
};
},

// 自定义统一返回值
response(service.config, response) {},
},
};

这样处理后在使用时只需要 service.get('@a/user/mm'); 即可,甚至配置里还可以根据当前的环境进行分发到不同的接口中,萌萌哒~

Node.js 代理

Service 层处理之后,如果使用 CORS 即可直接生效,但现在很多服务都是反向代理,可以使用 webpack 的 devServer.proxy ,如:

// config/proxy.js

/**
* 本地开发、测试配置
*
* @type {Object}
*/
const dev = {
'/api/a/': {
target: '目标开发环境',
changeOrigin: true,
onProxyReq(req) {
req.setHeader('origin', '目标开发环境');
},
},
};

/**
* 线上生产模式
*
* @type {Object}
*/
const prod = {
'/api/a/': {
target: '目标生产环境',
changeOrigin: true,
onProxyReq(req) {
req.setHeader('origin', '目标生产环境');
},
},
};

// 暴露 dev 代理
exports.dev = dev;

// 暴露生产代理
exports.prod = prod;

// 根据环境暴露不同的代理
exports.proxy = IS_PROD ? prod : dev;

在 devServer.proxy 直接引用对应的环境配置即可。

你会发现还配置了 onProxyReq ,是因为 changeOrigin 只是会修改请求的 host ,代码见:node-http-proxy ,而很多后端服务是有源 origin 限制,这样就可以穿透了。

注意:Service 是运行在浏览器端,而 Node.js 代理只是本地开发时为了接口方便进行的代理,Service 把 @a/ 标识替换成请求本地的绝对链接 /api/a/xxx (假如不是 CORS 外链域名),然后进入 Node.js 代理,而线上大部分都是 Nginx 做的代理 。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章