当前位置: 首页 > Web前端 > HTML5

Javascript是如何完全接管xhr请求的

时间:2023-04-05 17:01:43 HTML5

Javascript完全接管xhr请求后台思考为什么一定要接管xhr请求?这就需要我们去了解它的一些应用场景。我们如何在项目中统一xhr请求的行为,监控请求的整个生命周期,如何自定义拦截请求并返回mock数据,如何开发一个完全可控的控制台(如vconsole),如何监控健康所有api请求的状态等。等等!有一种最常见的情况。比如在项目中发起请求有不同的方式,有的是在jssdk或者私有npm库中发起,有的是在引入第三方jscdn中发起,有的是在项目中统一ajax和axios发起.如果我们需要为项目中的所有请求添加一些统一的行为怎么办?NativeXMLHttpRequest回顾使用xhr发起请求注意:以下仅针对xhr处理,不考虑使用ActiveXObject处理兼容性,不考虑使用fetch请求。//创建一个XMLHttpRequest对象varxhr=newXMLHttpRequest();//建立连接xhr.open(method,url,async,username,password);//打开之后,发送之前,可以处理消息,比如settingtherequestHeaderxhr.setRequestHeader('customId',666)//对于异步请求,绑定响应状态事件监听函数xhr.onreadystatechange=function(){//监听readyState状态,http状态码if(xhr.readyState==4&&xhr.status==200){console.log(xhr.responseText);//接收数据}}//使用send()方法发送请求xhr.send(body);//同步请求,可以直接接收数据console.log(xhr.responseText);//中止请求xhr.onreadystatechange=函数(){};//清理事件响应函数(IE、Firefox兼容性处理)xhr.abort();ES5实现部分拦截假设ajax、axios和nativexhr在请求时添加自定义请求头custom-trace-id:'aa,bb'。我们如何通过拦截获取它的值,新增两个请求头'custom-a':'aa'和'custom-b':'bb'(拆分custom-trace-id的值得到'aa'和'bb')拦截项目中所有的xhr,并在header中添加一个新的自定义请求头与'custom-trace'(只拦截open和setRequestHeader)(function(w){w.rewriteXhr={//随机生成的uuid_setUUID:function(){return'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g,function(c){varr=Math.random()*16|0,v=c=='x'?r:(r&0x3|0x8);returnv.toString(16);});},//存储xhr原型xhrProto:w.XMLHttpRequest.prototype,//存储需要的本地属性被拦截或者methodtempXhrProto:function(){this._open=this.xhrProto.openthis._setRequestHeader=this.xhrProto.setRequestHeader},//拦截处理proxy:function(){var_this=thisthis.xhrProto.open=function(){this._open_args=[...arguments];return_this._open.apply(this,arguments);}//拦截setRequestHeader方法this.xhrProto.setRequestHeader=function(){varheaderKey=arguments[0]//所有请求需要添加的headervarkeys=['custom-a','custom-b','custom-uuid']//url可用于过滤//varurl=this.open_args&&this.open_args[1]if(/^custom-trace-id$/.test(headerKey)){varvalue=arguments[1]&&arguments[1].split(',')value[2]=_this._setUUID()keys.forEach((key,index)=>{//你也可以使用_this._setRequestHeader。apply(this,arguments)this.setRequestHeader(key,value[index])})return}return_this._setRequestHeader.apply(this,arguments)}},初始化:function(){this.tempXhrProto()this.proxy()}}w.rewriteXhr.init()})(window)上面我们重新定义了open和setRequestHeader的原型方法(拦截open的目的是只获取该方法参数中的url等信息),同时也存储了原来的open和setRequestHeader。当有请求调用setRequestHeader时,我们实际上是在调用自己重写的setRequestHeader方法,在该方法中调用原来的setRequestHeader,从而达到拦截和设置请求头的目的。了解了本地的xhr拦截之后,我们可以以此来思考如何封装实现全局的请求拦截?ES5在项目中使用xhrHookxhrHook({open:function(args,xhr){console.log("opencalled!",args,xhr)},setRequestHeader:function(args,xhr){console.log("setRequestHeadercalled!",args,xhr)},onload:function(xhr){//处理响应结果this.responseText=xhr.responseText.replace('abc','')}})xhrHook的实现是全局的在拦截中,我们需要考虑实例的属性、方法和事件处理。functionxhrHook(config){//存储真正的xhr构造函数,当钩子被取消时,可以恢复window.realXhr=window.realXhr||XMLHttpRequest//重写XMLHttpRequest的构造函数XMLHttpRequest=function(){varxhr=newwindow.realXhr()//遍历实例及其原型上的属性(如果实例和原型链的属性相同,取实例属性)for(varattrinxhr){if(Object.prototype.toString.call(a)==='[objectFunction]'){this[attr]=hookFunction(attr);//接管xhr函数}else{Object.defineProperty(this,attr,{//接管xhrattr,eventget:getterFactory(attr),set:setterFactory(attr),enumerable:true})}}//The真正的xhr实例存放在自定义的xhr属性中this.xhr=xhr}}xhr中的方法拦截//xhr中的方法拦截,eg:open,sendetc.functionhookFunction(fun){returnfunction(){varargs=Array.prototype.slice.call(arguments)//将打开的参数存放在xhr中,可以在其他事件回调到达时获取。if(fun==='open'){this.xhr.open_args=args}if(config[fun]){//当配置的函数执行结果返回true时终止调用varresult=config[fun].call(this,args,this.xhr)if(result)返回结果;}返回this.xhr[fun].apply(this.xhr,args);}}xhr中属性和事件的拦截//属性和回调方法的拦截functiongetterFactory(){varvalue=this.xhr[attr]vargetter=(proxy[attr]||{})["getter"]returngetterHook&&getterHook(值,这个)||value}//赋值时触发工厂函数(如onload等事件)functionsetterFactory(attr){returnfunction(value){varxhr=this.xhr;var_this=这个;varhook=config[attr];//方法或对象if(/^on/.test(attr)){//将函数绑定到真实xhr上的事件xhr[attr]=function(e){e=configEvent(e,_this)varresult=hook&&hook.call(_this,xhr,e)结果||value.call(_this,e);}}else{varattrSetterHook=(hook||{})["setter"]value=attrSetterHook&&attrSetterHook(value,_this)||值尝试{xhr[attr]=值;}catch(e){console.warn('xhr'+attr+'属性不可写')}}}}解除xhr拦截,归还xhr管理权//归还xhr管理权functionunXhrHook(){if(window[realXhr])XMLHttpRequest=window[realXhr];window[realXhr]=undefined;}ES6深夜实现全局拦截,等待排序...总结一下xhr的整体情况拦截总体来说比较简单,只是事件承载过程有点复杂,无论是本地还是globalprocessing,共同的特点是必须存储原来的xhr,但是在执行na??tive属性、方法、事件时,会先执行自己的Process函数,在函数中进行一些操作,最后执行native方法。对于事件拦截,比如我们定义xhr.onload=function(){}时,真正触发的是我们自己定义的onloadsetter方法。在这个方法中,回调函数onload会绑定到真正的xhr,并在回调函数中执行config.onload中的逻辑。如果config.onload()没有返回或者返回false,就会继续执行之前绑定的xhr.onload函数。如果您有任何不足、问题或建议,请留言指出。作者:tager链接:https://juejin.cn/post/7019704757556084750说明:稀土掘金同步更新版权归作者所有。商业转载请联系作者授权,非商业转载请注明出处。