当前位置: 首页 > 科技观察

我们公司如何封装Axios来应对百万流量中很少见的问题

时间:2023-03-17 11:40:30 科技观察

本文是我们团队每周分享的内容,内容由导师整理分享。Eaxios是我们前端团队使用的库。是导师打包的。因为其他小伙伴也很好奇,所以只有这篇文章分享。正文开始~~EaxiosEaxios是一个基于axios封装的网络请求库。简化了服务端的响应内容和各种异常的处理,同时保持API与axios基本一致。开发背景如上图所示,是一个Ajax请求可能的输出结果。在前端,我们需要根据输出结果给用户不同的提示。请求取消:忽略网络异常:提示检查网络是否连通请求超时:提示网络慢,请切换到网络服务器异常:提示系统有问题是业务异常,并且前端需要根据错误码进行处理。最简单的就是一个消息提醒请求,请求成功:前端收到数据后更新界面。但是现有的Axios库并没有对异常结果进行很好的封装,AxiosPromisecatch中包含各种类型的错误,也没有提供错误码来识别请求失败的原因。而且很多服务端的接口都会返回自己的错误码,那么业务异常也需要在AxiosPromise中进行处理。此外,Axios本身有一些问题和限制,如下所述。如果设置axiosresponseType为json,则无法解析服务器返回的非JSON格式的响应内容,response.data为null。对于500等错误,响应内容会丢失,所以不要将responseType配置为json。对于用户来说很容易发现这个坑。ps:虽然axios官方文档说responseType是json,但实际上底层调用XMLHttpRequest的responseType是没有传值的,应该是为了避免这个问题。默认情况下axios不管HTTP响应状态和responseType都会调用默认的transformResponseps:应该是为了避免前面的问题,默认提供了一个response处理函数用于JSON解析,但是这样会影响性能(当有很多响应内容值如500,会导致页面卡顿)。transformResponse虽然可以对response进行转换,但是实际接收到的参数是response.data,所以无法判断具体情况来决定是否解析JSON。axiosthen和catch是根据validateStatus来判断的,用户处理起来比较麻烦。理想情况下,用户希望随后返回有效数据,并捕获返回各种错误情况:请求取消、网络异常、网络超时、服务器异常、服务器数据格式错误、业务异常。axios默认不处理content-type为application/x-www-form-urlencoded的requestbody,使用起来不方便。优化方案:如果设置axiosresponseType为json,则不要传递XMLHttpRequest,以免非JSON格式的响应内容丢失。axios根据响应头的content-type判断是否需要解析JSON,避免性能问题。通过请求拦截器,不将transformResponse配置传递给axios,将配置备份到其他字段,然后将响应对象存入响应拦截器。响应被传递给transformResponse进行处理。响应拦截器根据response提供的状态码、响应头和响应内容判断是否进行JSON转换。取消axiosvalidateStatus的配置选项。默认情况下,所有大于0的状态码都是正确的状态码,然后在axios拦截器中进行数据解析(非200也可能是json,所以200的json解析码要复用),并根据异常情况,抛出一个直观的错误对象。使用内置默认处理表单类型的请求体。eaxios主要优化了响应的处理。除了以下部分,eaxios的api与axios一致:eaxios请求配置的transformResponse参数以及处理时机发生了变化。服务器响应内容后axios会调用transformResponse进行响应转换。eaxios响应后,会根据响应头和responseType自动解析JSON,然后将解析后的数据和response传递给transformResponse,transformResponse返回的数据最终会通过Promise解析给外部调用者。假设服务器返回的数据结构为{code:0,message:'success',data:{}},code为0表示响应正确,非0表示异常。接口请求代码示例如下:consteaxios=require('eaxios');eaxios.defaults.transformResponse=[function(data,response){if(typeofdata==='object'){//默认协议就是JSON对象解析成功,认为服务端响应成功,有提供错误码if(data.code===0){returndata.data;}else{throwweaxios.createError(data.message,data.code,response);}}else{//50x等服务异常throweaxios.createError(data,response.config.responseError.SERVER_ERROR,response);}},];returnaxios('https://run.mocky.io/v3/4f503449-0349-467e-a38a-c804956712b7').then((数据)=>{console.log('成功',data.id);}).catch((错误)=>{console.log('failure',error.code);//UNKNOWN,REQUEST_OFFLINE,REQUEST_TIMEOUT,SERVER_ERROR,RESPONSE_INVALID和业务错误码});ps:如果有服务单接口请求规范,可以使用eaxios.create创建适合不同接口规范的请求函数。eaxios的请求处理函数将只接收transformResponse转换后的数据。对于网络、超时、服务器端异常和业务异常,它会在catch中收到一个EaxiosError类型的错误对象。interfaceEaxiosErrorextendsError{config:EaxiosRequestConfig;code?:string;request?:any;response?:EaxiosResponse;isAxiosError:boolean;toJSON:()=>object;}错误处理函数可以基于关于错误码code处理异常,code的可能值有UNKNOWN、REQUEST_OFFLINE、REQUEST_TIMEOUT、SERVER_ERROR、RESPONSE_INVALID等业务错误码。ps:如果想自定义错误码,可以在请求配置中添加配置项`responseError`。eaxios.defaults.responseError={REQUEST_OFFLINE:'1'REQUEST_OFFLINE};eaxios内部会自动序列化form类型的请求参数,所以将object传给data即可。代码示例下面以{code:0,message:'success',data:{}}等接口规范为例,演示如何使用eaxios。consteaxios=require('..');constrequest=eaxios.create({baseURL:'https://run.mocky.io/v3',timeout:30000,transformResponse:[function(data,response){if(typeofdata==='object'){if(data.code===0){returndata.data;}else{throweaxios.createError(data.message,data.code,response);}}else{throweaxios.createError(data,response.config.responseError.SERVER_ERROR,response,);}},],});request.interceptors.response.use(function(response){returnresponse;},function(error){if(error&&error.code){if(error.code==='UNKNOWN'){console.log('未知错误');}elseif(error.code==='REQUEST_OFFLINE'){console.log('网络未连接');}elseif(error.code==='REQUEST_TIMEOUT'){console.log('网络有点慢,请求超时');}elseif(error.code==='SERVER_ERROR'){console.log('系统有问题');}elseif(error.code==='RESPONSE_INVALID'){console.log('serverbug');}elseif(error.code==='10000'){//假设10000是登录会话过期console.log('登录会话无效');}else{console.log('根据情况,是要消息提示还是外部处理')}}throwerror;},);functionprintError(error){console.log(`code:${error.code},name:${error.name},message:${error.message},isAxiosError:${error.isAxiosError},stack:\n${error.stack}`,);}functionsuccess(){console.log('>>成功');returnrequest('/4f503449-0349-467e-a38a-c804956712b7').then((data)=>{console.log('success',data);}).catch((错误)=>{printError(error);});}functionfailure(){console.log('>>failure');returnrequest('/42d7c21d-5ae6-4b52-9c2d-4c3dd221eba4').then((data)=>{console.log('success',data);}).catch((error)=>{printError(error);});}functioninvalid(){console.log('>>invalid');returnrequest('/1b23549f-c918-4362-9ac8-35bc275c09f0').then((data)=>{console.log('success',data);}).catch((error)=>{printError(error);});}functionserver_500(){console.log('>>server_500');returnrequest('/2a9d8c00-9688-4d36-b2de-2dee5e81f5b3').then((data)=>{console.log('成功',data);}).catch((error)=>{printError(error);});}success().then(failure).then(invalid).then(server_500);/*log>>成功成功{id:1}>>失败登录会话失败代码:10000,名称:错误,消息:错误,isAxiosError:true,堆栈:...>>无效的服务器错误代码:RESPONSE_INVALID,名称:SyntaxError,消息:UnexpectedtokenVinJSONatposition0,isAxiosError:true,stack:...>>server_500系统代码有问题:SERVER_ERROR,name:Error,message:...,stack:...*/compatibilityeaxios依赖URLSearchParams处理表单类型的请求parameters,not支持的环境需要引入相应的polyfillcore-jsrequire("core-js/modules/web.url-search-params.js")url-search-params-polyfill【编辑推荐】HarmonyOS官方战略合作合作伙伴-construction-TechCommunityCloud中的HarmonyOS容器:您有哪些选择?5G消息全面进入发展期什么是域名劫持?如何应对域名劫持自学编程,首先应该选择什么语言?一篇文章读懂网络爬虫发展史