解决一个场景,在请求前,后做一些统一的事情
- 现代项目中请求往往是统一封装的例如在统一包含的axios方法中做一些操作
//添加token
axios.interceptors.request.use(
config => {
config.headers['token'] = 'xxxx'
return config
},
error => {
return Promise.reject(error)
}
)
//统一异常处理
axios.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
Message({
message: res.msg || '出错啦!',
type: 'error',
duration: 5 * 1000,
showClose: true
})
return Promise.reject(res.msg || 'error')
} else {
return res
}
},
error => {
Message({
message: '没事,只是服务器出错啦!',
type: 'error',
duration: 5 * 1000,
showClose: true
})
return Promise.reject(error)
}
)
引出话题,面向切片
- AOP就是在运行时,动态的将代码切入到类的指定方法的指定位置上
- 好处:模块解耦,不依赖,减少多余代码不需要在每个模块添加功能。
- 场景:日志,异常处理等等
- 应用例如:前端请求统一处理,nginx统一添加请求超时时间,统一插入特定资源,后端日志模块log4js
细分场景,回归主题
- 今天要解决的是请求和响应的问题。
- 的确在现在的模式下面能解决统一处理的问题,可如果多个页面内嵌,不同技术栈混用的场景呢?我们解决多个页面中特定某些请求,加密解密这样的一个场景呢?
大胆的猜测一下方案?
- 我使用的是Ajax-hook,这样的方案(git地址: github.com/wendux/Ajax…
- 实现原理:把本不可以修改的XMLHttpRequest,通过Object.defineProperty这样的属性进行修改
Object.defineProperty(this, attr, {
get: getterFactory(attr),
set: setterFactory(attr),
enumerable: true
})
*使用示例如下
/*
* @Description: 加密
* @Author: 吴文周
* @Github: http://gitlab.yzf.net/wuwenzhou
* @Date: 2020-03-13 17:39:01
* @LastEditors: 吴文周
* @LastEditTime: 2020-09-24 17:57:07
*/
(function () {
/**
* 需要拦截的url
*/
var urlArray = [''];
/**
* 解密数据
*/
function decryptResultAES(){
}
/**
* 转化数据返回
*/
function tryParseJson(v,xhr){
if (checkUrl(xhr.xhr._ProxyURL)) {
if (xhr.status === 200 && xhr.readyState === 4) {
if (xhr.xhr.responseText && JSON.parse(xhr.xhr.responseText).result) {
var responseText = JSON.parse(xhr.xhr.responseText);
var result = decryptResultAES(responseText.result);
if (typeof responseText.result !== 'object') {
responseText.result = JSON.parse(result);
return JSON.stringify(responseText);
}
}
}
}else{
return v;
}
}
/**
* 获取无后缀url
* @param {*} url
*/
function getUrl(url) {
return url.indexOf('?') > 0 ? url.substring(0, url.indexOf('?')) : url;
}
/**
* 判断数组是否包含字符串
* @param {*} url
*/
function checkUrl(url) {
return !!url? urlArray.indexOf(url) >= 0:false;
}
/**
* 劫持请求
*/
hookAjax({
responseText: {
getter: tryParseJson
},
response: {
getter:tryParseJson
},
//拦截回调
onreadystatechange: function (xhr) {
},
onload:function(xhr){
},
//拦截方法
open: function (arg, xhr) {
var url = getUrl(arg[1]);
// 通过篡改添加标识地址
Object.defineProperty(xhr, '_ProxyURL', {
value: url,
writable: true // 是否可以改变
})
},
send: function (arg, xhr) {
if (checkUrl(xhr._ProxyURL)) {
xhr.setRequestHeader("xxx", "xxx")
}
}
})
})()
- 通过这样的js引入就可以在各个页面加入加密解密不需要改变任何业务代码。
升华一下
- 这样的方式还是需要通过页面引入js的方式实现,虽然已经做得很好了,但是不能满足现状,再进一步。
- nginx中就可以配置 nginx.org/en/docs/htt…
sub_filter </head> "<script>xxx</script></head>"
- 这样就不需要修改任何代码即可实现这样的功能,但是生产慎重,把业务设置到运维端是不太可控的,只是这样的方式比较简单而已
百尺竿头更进一步
- 我们能做的不仅仅如此,比如刚才加密的问题,我本地开发和后端开发都把代码提交测试环境了,加密方式修改了,可是很多小伙伴代码是在我当前的分支时间点之前提交了,我也并没有把代码提交到master这样一个时间差问题。
- 问题二测试人员在测试的时候发现接口加密了不能跟页面数据一一对应了。
- 问题三接口本身如果做了接口转化,变成了/api/0001 这样的本身示意性就很弱了。
- 这显然阻碍了测试人员以及上线以后也没法查看调试。也就出现了这样的需求,我想在加密的时候加密,解密的时候解密。内部人员都是无加密的状态,外部客户都是加密不可见。同一个浏览器怎么实现请求响应不一样呢?
- 想一想浏览器是不是也是一个切片呢?浏览器还是真是一个切片,通过谷歌插件实现。
- 只要内部人员安装了谷歌插件,劫持请求是不是就实现以上的功能。
谷歌插件我来了
- 项目地址: github.com/fodelf/easy…
- 谷歌插件可以实现劫持请求,包括资源和其他请求
- 举例在请求到匹配的js后,我把资源指向修改,是不是就可把加密解密的那个js进行篡改到内容的服务器某个静态资源地址。这个js可以将接口地址,解密信息完全解密到控制台输出,或者我想修改线上的任意资源js,css指向到自己的地址。
- 请求修改:同样的我可以借助于之前的ajax-hook这样的库就可以实现修改请求的任意属性,请求头信息,例如请求url等等,应用场景包括在爬取某些网站时篡改里面的不同参数,找出不同参数对应的页面映射,所有请求响应都由我这个插件控制,我想页面渲染什么内容都可以。
开发注意事项
- 谷歌插件是通过资源添加到谷歌浏览器中,在开发过程中使用 "build": "vue-cli-service build --watch",监听变化实时编译,然后在谷歌扩展管理里面刷新资源,这样开发方便。
- 关于谷歌插件操作功能的js不需要放到vue的src中,放在静态资源目录即可,如需编译可自行写脚本。
- manifest.json中各种配置项,关键是权限配置项content_scripts 这是一种插入的方式。
- 通过storage进行数据共享。
- 通过postMessage进行消息通信。
资源地址
- 谷歌插件篡改请求: github.com/fodelf/easy…
- ajax-hook拦截请求: github.com/wendux/Ajax…
- nginx配置: nginx.org/en/docs/htt…
总结
- 如果有一个事情需要在不同的模块中重复做,如果现在就改几个地方也行,再想一想我们可以设计成面向切片,就可以实现任他雨打风吹,我不动如山,笑看风云淡。
结束
- 加油!打工人!