赞
踩
什么是抓包:抓包就是抓取网络请求,将请求一直挂在到我们写的中间件中,在中间件统一修改后完成本次请求,俗称请求托管
闲着没事本篇文章用js给大家手写一个抓包工具,配合油猴脚本嵌入各大网站,仅供学习参考、本地测试,切勿实施违法行为,现成脚本代码在最后,老规矩先看图脚本图一目了然,脚本也是我之前自己写的,大家拿去用即可
抓包的作用有哪些?
1、拦截请求,修改请求体,篡改一些我们无法手动更改请求信息
2、拦截响应,修改响应体,对无防御无加密式网站可以造成一定影响,比如解锁一些功能
3、看执行逻辑和说要做的事务(回调事件)
4、方便开发测试
值得一提的是,网页抓包方式比较局限,我们悉知前端向服务器发送请求只有几种方式,原生XMLHTTPRequest、Fetch、Axios(基于前两者混合封装),其中Axios是对请求的进一步封装,底层依旧和前两者挂钩,因此我们只需要了解原生Ajax和Fetch
想要在收发中间横插一脚,只能依靠中间件,今天我们可以手写一个中间件,我会以原生Ajax为例向大家讲述,至于Fetch,看完文章之后原理完全一样,甚至你可以亲自手写,我们知道在java中可以重写和继承各种类来修改代码,但在js中显然没有完整的重写概念,我们只能改变其指向
首先我们要拦截网站发出的请求,托管到我们编写的代码,这一步怎么做到呢?仔细想一想,使用原生Ajax发送请求的第一步是什么?
1、创建XMLHTTPRequest实例
2、调用open方法控制请求流程
3、调用send方法发送请求
所以我们的思路就来了,先改变其指向,发送请求时网站会自实例化XMLHTTPRequest进行交互,我们捷足先登
middle.js
- //开始编写我们的中间件类
- class XMLHTTPRequestMiddle {
- constructor() { }
-
- open(method, url) {
- console.log('请求地址:', url, '请求方式', method)
- }
-
- send(data) {
- console.log('我是请求体', data)
- }
- }
-
- //我们将XMLHTTPRequest指向到我们自己写的类
- window.XMLHttpRequest = XMLHTTPRequestMiddle
index.html
- <script src="/middle.js"></script>
- <script>
-
- const request = new XMLHttpRequest()
-
- request.open('get', 'http://localhost:8060/test')
-
- request.send()
-
- </script>
别急,我们先启动一下82年没打开过的SpringBoot项目,写一个测试接口,让后端返回一个对象
- import org.springframework.web.bind.annotation.GetMapping;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import java.util.HashMap;
- import java.util.Map;
-
- @RestController
- @RequestMapping("/test")
- public class TestController {
- @GetMapping
- public Map<String, String> test() {
- HashMap<String, String> result = new HashMap<>();
- result.put("code", "200");
- result.put("message", "success");
- result.put("data", "no data");
- return result;
- }
- }

结果在我们意料之中,成功拦截到关键信息,url,method,以及body对象(请求体),随之而来又有问题了,请求发送过程中被我们成功拦截,但是还少了关键的方法onreadystatechange,这才是我们发送请求之后最重要的方法,他将决定实现什么功能,如何拿数据,这个方法我们怎么写?不能直接像open和send那样直接写,因为onreadystatechange方法里面有个默认参数比较重要,这个参数是我们无法一开始传入的,发送请求后才能获取到的信息,那么我们怎么做?
一、onreadystatechange方法是什么?
通俗的来讲,他就是发送请求之后我们要干的事,就写在里边,可以看作Promise的then方法,then方法里面执行你操作页面的逻辑,也是一个回调函数,一般从这里拿到后端传回来的数据供页面渲染,举个例子
- request.onreadystatechange = (event) => {
- //此方法有个默认参数 可以从参数拿到当前请求的字节流长度等 实现加载进度等
- //但是目前这个参数我们写的中间件是无法模拟给出的
- document.write(request.responseText)
- }
通过观察我们发现onreadystatechange是个setter,setter怎么拿,我们在中间件定义一个setter,setter的特性必须传入至少一个参数,先拿到setter后面肯定有大用场,在中间件加入onreadystatechange的setter方法,直接上代码
- class XMLHTTPRequestMiddle {
- constructor() { }
-
- open(method, url) {
- console.log('请求地址:', url, '请求方式', method)
- }
-
- send(data) {
- console.log('我是请求体', data)
- }
-
- //加入同名setter
- set onreadystatechange(callback) {
- //注意 setter内的参数 就是我们上图写的页面输出回调 打印就一目了然了
- console.log(callback)
- }
- }
'运行
结果如出一辙,非常完美,但是onreadystatechange的参数我们仍然无法模拟,上面拿到的setter参数实际上是XMLHTTPRequest本体中onreadystatechange的映射,我们需要做的是,得到参数,然后传入刚才setter到的callback中执行,怎么做?想到了吗?还有构造啊,构造我们晾了半天,也该干点实事了,干他!
- //其余代码略
- constructor() {
- //我们定义一个callback属性 来映射真正的onreadystatechange 这样可以拿到两个参数并执行
- this.callback = null
- }
-
- //加入同名setter
- set onreadystatechange(callback) {
- //注意 setter内的参数 就是我们上图写的页面输出回调 打印就一目了然了
- //console.log(callback)
- if (typeof callback == 'function') {
- this.callback = callback
- } else {
- throw new Error('不是函数憋来沾边')
- }
- }

到这一步,还是没有实质性的动作,因为我们并没有真正发送请求!就连请求收集都没做好,慢慢来,下面看我操作
到这里需要收集请求了 ,不建议在构造内定义请求队列(requestQueue),我们提到全局
- //在全局定义请求队列 保存每次完整实例后的请求体
- const requestQueue = []
-
- //保留一份本体
- const xmlhttp = XMLHttpRequest
-
- //------------------------------------------------------------------------------------------
-
- //其余代码略 在构造加入
- constructor() {
- this.callback = null
-
- //加入一个载体 来收集每次实例的请求体 他与requestQueue 紧密联系 为什么写一个header空对象在内
- //对于空对象的顶层添加 如:this.polay.url = 'xxx'不会报错
- //但对于次要层添加就会报错 如:this.polay.header.ContentType = 'json' ==> error...
- //因为他会认为已经存在header对象,会直接从header对象添加属性 但我们知道请求体内的请求头是个属性很多的对象
- this.polay = { header: {} }
-
- //加入Ajax本体来控制请求的真正发送 很关键
- this.request = new xmlhttp()
- }

开始写数据收集,碎片化的收集最终会组合成一个完整请求体(request),付上完整代码防止断片
- //在全局定义请求队列 保存每次完整实例后的请求体
- const requestQueue = []
-
- //保留一份本体
- const xmlhttp = XMLHttpRequest
-
- //开始编写我们的中间件类
- class XMLHTTPRequestMiddle {
- constructor() {
- this.callback = null
-
- //加入一个载体 来收集每次实例的请求体 他与requestQueue 紧密联系 为什么写一个header空对象在内
- //对于空对象的顶层添加 如:this.polay.url = 'xxx'不会报错
- //但对于次要层添加就会报错 如:this.polay.header.ContentType = 'json' ==> error...
- //因为他会认为已经存在header对象,会直接从header对象添加属性 但我们知道请求体内的请求头是个属性很多的对象
- this.polay = { header: {} }
-
- //加入Ajax本体来控制请求的真正发送 很关键
- this.request = new xmlhttp()
-
- //加入responseText的映射
- this.responseText = null
-
- //其他属性 原生Ajax有什么加什么 这里暂时用上面若干属性做演示
- }
-
- open(method, url) {
- this.polay.url = url
- this.polay.method = method
- }
-
- //send总是在最后执行的 因此在这里 请求体已经收集完了 可以加入队列了
- send(data) {
- this.polay.body = data
-
- //这里是修改请求request的关键位置 写个小例子 比如修改请求方式Method
- const method = prompt(`请输入要修改的method方法,目前是${ this.polay.method }`)
- //达到修改效果
- this.polay.method = method
-
- //加入队列 现在明白为什么写个载体了吗?
- requestQueue.push(this.polay)
-
- //打印队列看是否抓取成功
- console.log(`request is`)
- console.log(requestQueue)
- }
-
- //加入同名setter
- set onreadystatechange(callback) {
- //注意 setter内的参数 就是我们上图写的页面输出回调 打印就一目了然了
- //console.log(callback)
- if (typeof callback == 'function') {
- this.callback = callback
- } else {
- throw new Error('不是函数憋来沾边')
- }
- }
-
- //设置请求头的方法 原生的 我们重写
- setRequestHeader(key, value) {
- this.polay.header[key] = value
- }
- }
-
- //我们将XMLHTTPRequest指向到我们自己写的类
- window.XMLHttpRequest = XMLHTTPRequestMiddle

我们通过弹窗把原本get改为post看是否生效
值得一提的是,上面我使用了弹窗简单模拟修改request操作,实际上你可以搭建界面,随心修改,通过按钮精准修改每一个值,篇幅有限,简陋教学,码不在多,有懂王则灵
厉不厉害?你坤哥,都干到这一步了,但我们页面仍然没有任何输出,我们还是没有真正发送数据,只是简单做了request的数据收集,来吧,真正发送请求,上代码
- //在全局定义请求队列 保存每次完整实例后的请求体
- const requestQueue = []
-
- //保留一份本体
- const xmlhttp = XMLHttpRequest
-
- //开始编写我们的中间件类
- class XMLHTTPRequestMiddle {
- constructor() {
- this.callback = null
-
- //加入一个载体 来收集每次实例的请求体 他与requestQueue 紧密联系 为什么写一个header空对象在内
- //对于空对象的顶层添加 如:this.polay.url = 'xxx'不会报错
- //但对于次要层添加就会报错 如:this.polay.header.ContentType = 'json' ==> error...
- //因为他会认为已经存在header对象,会直接从header对象添加属性 但我们知道请求体内的请求头是个属性很多的对象
- this.polay = { header: {} }
-
- //加入Ajax本体来控制请求的真正发送 很关键
- this.request = new xmlhttp()
-
- //加入responseText的映射
- this.responseText = null
-
- //其他属性 原生Ajax有什么加什么 这里暂时用上面若干属性做演示
- }
-
- open(method, url) {
- this.polay.url = url
- this.polay.method = method
- }
-
- //send总是在最后执行的 因此在这里 请求体已经收集完了 可以加入队列了
- send(data) {
- this.polay.body = data
-
- //这里是修改请求request的关键位置 写个小例子 比如修改请求方式Method
- const method = prompt(`请输入要修改的method方法,目前是${ this.polay.method }`)
- //达到修改效果
- this.polay.method = method
-
- //加入队列 现在明白为什么写个载体了吗?
- requestQueue.push(this.polay)
-
- //打印队列看是否抓取成功
- console.log(`request is`)
- console.log(requestQueue)
-
- //从这里开始发送请求 已经是通过上述修改过的request 我们已经实现拦截并修改request了
- this.request.open(this.polay.method, this.polay.url)
- this.request.send()
-
- //这里更关键 终于到我们一开始讲的那个参数那里了 在这一步可以解决了 这里决定是否执行回调
- this.request.onreadystatechange = (event) => {
- //暂时映射一种返回结果类型
- this.responseText = this.request.responseText
- this.callback(event) //参数问题完美传入
- }
- }
-
-
- //加入同名setter
- set onreadystatechange(callback) {
- //注意 setter内的参数 就是我们上图写的页面输出回调 打印就一目了然了
- //console.log(callback)
- if (typeof callback == 'function') {
- this.callback = callback
- } else {
- throw new Error('不是函数憋来沾边')
- }
- }
-
- //设置请求头的方法 原生的 我们重写
- setRequestHeader(key, value) {
- this.polay.header[key] = value
- }
- }
-
- //我们将XMLHTTPRequest指向到我们自己写的类
- window.XMLHttpRequest = XMLHTTPRequestMiddle

- <script src="/middle.js"></script>
- <script>
-
- const request = new XMLHttpRequest()
-
- request.open('get', 'http://localhost:8060/test')
-
- request.send()
-
- request.onreadystatechange = (http, event) => {
- document.write(request.responseText)
-
- console.log(event)
- }
-
- </script>

完美解决问题,服务端参数也输出了,event参数也拿到了,那响应拦截如何做呢?
- //全局加入响应队列
- const responstQueue = []
-
- //其余代码略
- send(data) {
- this.polay.body = data
-
- //这里是修改请求request的关键位置 写个小例子 比如修改请求方式Method
- const method = prompt(`请输入要修改的method方法,目前是${ this.polay.method }`)
- //达到修改效果
- this.polay.method = method
-
- //加入队列 现在明白为什么写个载体了吗?
- requestQueue.push(this.polay)
-
- //打印队列看是否抓取成功
- console.log(`request is`)
- console.log(requestQueue)
-
- //从这里开始发送请求 已经是通过上述修改过的request 我们已经实现拦截并修改request了
- this.request.open(this.polay.method, this.polay.url)
- this.request.send()
-
- //这里更关键 终于到我们一开始讲的那个参数那里了 在这一步可以解决了 这里决定是否执行回调
- this.request.onreadystatechange = (event) => {
- //暂时映射一种返回结果类型
- this.responseText = this.request.responseText
-
- //这里决定拦截响应体
- const responst = prompt('请输入新的data')
- //简陋模拟修改
- this.request.responseText = '新的数据'
-
- //最终输出到页面的是你已经修改过得数据
- this.callback(event) //参数问题完美传入
- }
- }

so?是不是很简单,什么?还不是很理解?!哎,怎么说你好呢,给你准备了现成代码啦,快点拿去!
请直接粘贴到油猴脚本内!没有油猴也没事,随便打开一个网页打开控制台把代码复制进去即可
- // ==UserScript==
- // @name New Userscript
- // @namespace http://tampermonkey.net/
- // @version 0.1
- // @description try to take over the world!
- // @author You
- // @match https://*/*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant none
- // ==/UserScript==
-
- /**
- * XMLHTTPRequest 拦截工具
- *
- * 大致思路 手写虚拟XMLHTTPRequest类 让真正的XMLHTTPRequest指向我们自己写的类
- * 再把一系列方法 请求地址 请求头 response预先收集起来集中做处理 最后再触发由各大网站自定义的回调函数实现他们的业务
- * 但是数据已经经过我们的加工处理了 Understand?
- *
- * 因为是全自动脚本 我们不得不把Html也集成到了js代码中混合开发 核心代码不多 Html他么的多啊
- */
-
- const request = XMLHttpRequest
-
- //请求拦截队列
- const RequestQueue = []
-
- //响应拦截队列
- const ResponseQueue = []
-
- class XMLHttpRequestPlus {
- constructor() {
- this.request = new request()
- //承载体 修改数据关键
- this.paloy = { header: {} }
- //核心方法 决定可不可以实现响应拦截
- this.requestCallback = null
- this.status = 0
- this.readyState = 0
- this.response = null
- this.responseText = null
- this.responseURL = null
- this.responseXML = null
- this.responseType = ''
- this.timeout = 1*60*60*24 //超时时间设为一天 强制解除各大网站超时不再有响应的安全策略
- }
-
- open(method, url) {
- this.paloy.method = method
- this.paloy.url = url
- this.paloy.params = url.indexOf('?') != -1 ? url.substring(url.indexOf('?') + 1).split('&').reduce(
- (obj, item) => {
- const keyVal = item.split('=')
- if (keyVal.length == 1) { obj[keyVal[0]] = '' } else { obj[keyVal[0]] = keyVal[1] }
- return obj
- }, {}) : null
- }
-
- //请求拦截核心部分
- send(body) {
- if(this.timeout < 1*60*60*24) this.timeout = 1*60*60*24 //防止对超时时间的更改
- this.paloy.data = body
- this.paloy.state = this.request.status
- this.paloy.ready = this.request.readyState
- this.paloy.index = RequestQueue.length
- this.paloy.invoke = this.requestCallback
- //加入受理队列
- RequestQueue.push(this.paloy)
- //渲染请求拦截表格
- ResolveRequestRow(this.paloy)
- //自动滚动到底部
- autoScroll()
- //在放行之前还有一件事要做 确认用户修改我们的请求体
- const xmlConfrimBtn = [...document.querySelectorAll('.xml-http-request-more-message-footer')]
- xmlConfrimBtn.forEach((element, key) => {
- element.querySelector('button').onclick = () => {
- const { parentNode } = element.parentNode
- const url = parentNode.querySelector('.request-url').querySelector('textarea').value
- const method = parentNode.querySelector('.data-list-select').value
- const header = parentNode.querySelector('.request-header').querySelector('textarea').value
- const params = parentNode.querySelector('.request-params').querySelector('textarea').value
- const invoke = parentNode.querySelector('.request-invoke').querySelector('textarea').value
- try {
- const paramsed = JSON.parse(params)
- RequestQueue[key].url = url.indexOf('?') != -1 ? `${ url }&${ Object.entries(paramsed).map(([key, value]) => `${ key }=${ value }`).join('&') }`
- : `${url}?${Object.entries(paramsed).map(([key, value]) => `${ key }=${ value }`).join('&') }`
- } catch (error) {
- RequestQueue[key].url = url
- }
- console.log(url)
- RequestQueue[key].method = method
- RequestQueue[key].header = JSON.parse(header)
- RequestQueue[key].params = JSON.parse(params)
- //暂不开放修改回调功能 因为目前没有完美的修改方案
- //this.requestCallback = new Function(invoke)
- }
- })
- //放行事件 也是核心部分 决定了能不能拿到AJAX的状态以及返回值
- const nextRun = [...document.querySelectorAll('.request-next-run')]
- nextRun.forEach((element, key) => {
- element.onclick = () => {
- this.request.open(RequestQueue[key].method, RequestQueue[key].url)
- //这里放行请求头
- //this.request.setRequestHeader('Content-Type', 'application/json')
- Object.entries(RequestQueue[key].header).forEach(([key, value]) => { this.request.setRequestHeader(key, value) })
- this.request.send(body)
- this.request.onreadystatechange = (event) => {
- //先将response托管到响应队列保管 以便劫持修改
- if (this.request.status >= 200 && this.request.readyState == 4) {
- ResponseQueue.push({
- method : this.paloy.method,
- url : this.paloy.url,
- status : this.request.status,
- readyState : this.request.readyState,
- response : this.request.response,
- responseText : this.request.responseText,
- responseURL : this.request.responseURL,
- responseXML : this.request.responseXML
- })
- //紧接着执行响应拦截表格
- ResolveResponseRow(ResponseQueue[ResponseQueue.length - 1])
- }
- //响应数据修改
- const setResponseDataBtn = [...document.querySelectorAll('.xml-http-response-more-message-footer')]
- setResponseDataBtn.forEach((element, key) => {
- element.querySelector('button').onclick = () => {
- const { parentNode } = element.parentNode
- const status = parentNode.querySelector('.response-status').querySelector('input').value
- const data = parentNode.querySelector('.response-data').querySelector('textarea').value
- const text = parentNode.querySelector('.response-text').querySelector('textarea').value
- ResponseQueue[key].status = status
- ResponseQueue[key].response = data
- ResponseQueue[key].responseText = text
- }
- })
- //响应拦截放行
- const nextRun = [...document.querySelectorAll('.response-next-run')]
- nextRun.forEach((element, key) => {
- element.onclick = () => {
- const { method, url, status, readyState, response, responseText, responseURL, responseXML, index } = ResponseQueue[key]
- this.status = status
- this.readyState = readyState
- this.response = this.responseType == 'json' ? JSON.parse(response) : response
- this.responseText = responseText
- this.responseURL = responseURL
- this.responseXML = responseXML
- //最终返回响XMLHTTPRequest接收处理
- this.requestCallback(event)
- //放行之后此处响应就没用了 不能再次响应给XMLHTTPRequest 所以要移除掉DOM
- removeRows(element.parentNode.parentNode, index)
- }
- })
- }
- }
- })
- }
-
- setRequestHeader(name, value) { this.paloy.header[name] = value }
-
- getResponseHeader(name) { return this.request.getResponseHeader(name) }
-
- getAllResponseHeaders() { return this.request.getAllResponseHeaders() }
-
- overrideMimeType(mime) { this.request.overrideMimeType(mime) }
-
- async abort() {
- //alert(`检测到${ (await RequestQueue)[RequestQueue.length - 1]['url'] }试图终止请求来保护网站,已被分析大师驳回`)
- }
-
- set ontimeout(func) { return }
-
- set onreadystatechange(func) { if (typeof func == 'function') this.requestCallback = func }
- }
-
-
- //切换为手动抓包
- window.handMovementPacketCapture = function handMovementPacketCapture() { window.XMLHttpRequest = XMLHttpRequestPlus }
-
- /*
- * !这里请注意 如果想嵌入脚本即抓取 请打开下面的注释
- */
- // window.XMLHttpRequest = XMLHttpRequestPlus
-
- const app = document.querySelector('body'), header = document.querySelector('head')
-
-
- /**
- * 控制面板UI
- */
- function ControllerTemplate() {
- return `
- <div class="xml-http-intercepter-controller-template">
- <div class="intercepter-controller-button">${ Button() }</div>
- <div class="intercepter-controller-content">${ Header() }
- <div class="intercepter-controller-main">
- <div class="main-header">${ MinHeader() }</div>
- ${ DataView() }
- </div>
- </div>
- </div>
- `
- }
-
-
- /**
- * 按钮
- */
- function Button() { return `<button onclick="activeWindow()">START</button>` }
-
-
- //打开关闭调试窗口
- window.activeWindow = function activeWindow() { const wins = document.querySelector('.intercepter-controller-content'); wins.classList.toggle('content-show') }
-
-
- /**
- *
- * 头部
- */
- function Header() {
- return `
- <div class="controller-content-header">
- <div class="content-header-left">
- <div class="header-left-title">
- <h2>WEB - 请求分析大师</h2>
- </div>
- </div>
- <div class="content-header-right">
- <div class="header-right-options">
- <div class="option-item" onclick="activeWindow()">最小化</div>
- <div class="option-item">关于脚本</div>
- <div class="option-item">主题</div>
- <div class="option-item">设置</div>
- </div>
- </div>
- </div>
- `
- }
-
- /**
- * Main头部
- */
- function MinHeader() {
- return `
- <div class="main-header-left">
- <button class="main-tab-item main-tab-item-active" onclick="checkDataView(0)">请求拦截-Request</button>
- <button class="main-tab-item" onclick="checkDataView(1)" title="使用此功能前请确保进行了请求拦截">响应拦截-Response</button>
- </div>
- <div class="main-header-right">
- <button class="main-tab-item main-tab-item-active" title="默认不进行分析" onclick="checkAction(0)">关闭</button>
- <button class="main-tab-item" title="请手动触发网页请求以继续分析" onclick="checkAction(1)">手动分析</button>
- <button class="main-tab-item" onclick="checkAction(2)">全盘分析</button>
- </div>
- `
- }
-
-
- //切换左侧tab Method
- window.checkDataView = function checkDataView(n) {
- const leftItem = document.querySelector('.main-header-left').querySelectorAll('.main-tab-item')
- const dataItem = document.querySelectorAll('.intercepter-item')
-
- dataItem.forEach((element, index) => { leftItem[index].classList.remove('main-tab-item-active'); element.classList.add('item-close') })
-
- leftItem[n].classList.add('main-tab-item-active'); dataItem[n].classList.remove('item-close')
- }
-
-
- //切换右侧tab Method
- window.checkAction = function checkAction(n) {
- const leftItem = document.querySelector('.main-header-right').querySelectorAll('.main-tab-item')
-
- leftItem.forEach((element, key) => { element.classList.remove('main-tab-item-active') })
-
- leftItem[n].classList.add('main-tab-item-active')
-
- if (n == 0) window.XMLHttpRequest = request; else if (n == 1) handMovementPacketCapture(); else overallPacketCapture()
- }
-
-
- /**
- * 信息区域
- */
- function DataView() {
- return `
- <div class="intercepter-item">
- ${ ResolveTableHeader(
- [
- 'Method-请求方式', 'Origin-源地址', 'URL-请求地址', 'Params-参数', 'Body-请求体', 'Header-请求头', 'State-当前状态', 'Ready-就绪状态', 'Action-操作'
- ]
- ) }
- </div>
- <div class="intercepter-item">
- ${ ResolveTableHeader(
- [
- 'Method-请求方式', 'URL-请求地址', 'Status-状态码', 'Data-响应数据', 'Text-响应文本', 'URL-响应地址', 'XML-响应文档', 'Action-操作'
- ]
- ) }
- </div>
- `
- }
-
-
- /**
- * 信息表格
- */
- function ResolveTableHeader(data) {
- return `
- <table class="intercepter-table">
- <tbody>
- <tr class="table-header">
- <td><input type="checkbox"></td>${ data.map(title => `<td>${ title }</td>`).join('') }
- </tr>
- </tbody>
- </table>
- `
- }
-
-
- /**
- * 请求拦截行渲染
- */
- function ResolveRequestRow(option) {
- const table = document.querySelector('.intercepter-table')
- const { method, url, params, data, header, index, state, ready, invoke } = option
- table.insertAdjacentHTML('beforeend', `
- <tbody class="info-message">
- <tr>
- <td><input type="checkbox"></td>
- <td><span>${ method }</span></td>
- <td>${ url }</td>
- <td>${ url }</td>
- <td>${ params }</td>
- <td>${ data }</td>
- <td>${ header }</td>
- <td>${ state == 0 ? '劫持中' : '已放行' }</td>
- <td>${ ready == 0 ? '劫持中' : '已就绪' }</td>
- <td class="actions-button-group">
- <button class="error" onclick="removeRows(this.parentNode.parentNode, ${ index }, 'request')">忽略</button>
- <button class="info" onclick="openMoreMessage(this.parentNode.parentNode.parentNode)">详情</button>
- <button class="info request-next-run">放行</button>
- </td>
- </tr>
- </tbody>
- <tbody class="more-about-message">
- <tr>
- <td colspan="10">
- <div class="request-message-container">
- <div class="request-url">
- <div class="request-message-title"><h5>URL - 请求地址详情</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea placeholder="请输入请求地址">${ url }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="request-methods">
- <div class="request-message-title"><h5>Method - 请求方式详情</h5></div>
- <div class="request-message-content">
- <input
- class="data-list-select"
- list="data-list-${ index }"
- value="${ method }"
- placeholder="请选择方式"
- >
- <datalist id="data-list-${ index }">
- <option>Get</option>
- <option>Post</option>
- <option>Put</option>
- <option>Delete</option>
- <option>Path</option>
- </datalist>
- </div>
- </div>
- <div class="request-params">
- <div class="request-message-title"><h5>Params - 参数详情</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请编形式参数">${ JSON.stringify(params) }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="request-header">
- <div class="request-message-title"><h5>Header - 请求头详情</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请编辑请求头">${ JSON.stringify(header) }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="request-data">
- <div class="request-message-title"><h5>Body - 请求体详情</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请输入请求体参数">${ data }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="request-invoke">
- <div class="request-message-title"><h5>Invoke - 功能详情</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请输入具体实现代码">${ invoke }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="xml-http-request-more-message-footer">
- <button>确认修改</button>
- </div>
- </div>
- </td>
- </tr>
- </tbody>
- `)
- }
-
- /**
- * 响应拦截行渲染
- */
- function ResolveResponseRow(option) {
- const table = document.querySelectorAll('.intercepter-table')[1]
- const { method, status, url, response, responseText, responseXML, responseURL, index } = option
- table.insertAdjacentHTML('beforeend', `
- <tbody class="info-message">
- <tr>
- <td><input type="checkbox"></td>
- <td><span>${ method }</span></td>
- <td>${ url }</td>
- <td>${ status }</td>
- <td>${ response }</td>
- <td>${ responseText }</td>
- <td>${ responseURL }</td>
- <td>${ responseXML }</td>
- <td class="actions-button-group">
- <button class="error" onclick="removeRows(this.parentNode.parentNode, ${ index }, 'response')">忽略</button>
- <button class="info" onclick="openMoreMessage(this.parentNode.parentNode.parentNode)">详情</button>
- <button class="info response-next-run">放行</button>
- </td>
- </tr>
- </tbody>
- <tbody class="more-about-message">
- <tr>
- <td colspan="9">
- <div class="request-message-container">
- <div class="response-url">
- <div class="request-message-title"><h5>URL - 响应地址</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea placeholder="请输入请求地址">${ url }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="response-status">
- <div class="request-message-title"><h5>Status - 响应码</h5></div>
- <div class="request-message-content">
- <input class="data-list-select" value="${ status }" placeholder="请输入状态码">
- </div>
- </div>
- <div class="response-data">
- <div class="request-message-title"><h5>Response - 响应数据</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请输入响应参数">${ response }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="response-text">
- <div class="request-message-title"><h5>Text - 响应文本</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请输入响应文本">${ responseText }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="response-url">
- <div class="request-message-title"><h5>URL - 响应地址</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请输入响应地址参数">${ url }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="response-xml">
- <div class="request-message-title"><h5>XML - 响应文档</h5></div>
- <div class="request-message-content">
- <form class="request-message-form">
- <li>
- <textarea autoHeight="true" placeholder="请写入响应文档">${ responseXML }</textarea>
- </li>
- </form>
- </div>
- </div>
- <div class="xml-http-response-more-message-footer">
- <button>确认修改</button>
- </div>
- </div>
- </td>
- </tr>
- </tbody>
- `)
- }
-
-
- //展开详细信息
- window.openMoreMessage = function openMoreMessage(parent) { const nextElement = parent.nextElementSibling; nextElement.classList.toggle('more-message-show') }
-
-
- app.insertAdjacentHTML('beforeend', ControllerTemplate())
-
- // Methods
- // 自动滚动
- function autoScroll() {
- const contariner = document.querySelectorAll('.intercepter-item'), isActive = [...contariner].findIndex((em => !em.classList.contains('item-close')))
-
- contariner[isActive].scrollBy({ top: contariner[0].scrollHeight, left: 0, behavior: 'smooth' })
- }
-
- //删除一行
- window.removeRows = function removeRows(node, index, how) {
- const root = node, nextNode = node.parentNode.nextElementSibling, parentRoot = nextNode.parentNode
- root.classList.add('row-item-remove')
-
- how === 'request' ? delete RequestQueue[index] : delete ResponseQueue[index]
-
- setTimeout(() => { parentRoot.removeChild(nextNode); parentRoot.removeChild(root.parentNode) }, 380)
- }
-
- //改变请求方式
- window.changeMethods = function changeMethods(element) {
- alert(element.value)
- }
-
- //一些初始化启动
- checkDataView(0)
-
-
- /**
- * 样式
- */
- const STYLE = `
- <style>
- .xml-http-intercepter-controller-template {
- --bgcolor: rgb(157, 0, 255)/*rgb(255, 76, 76)*/
- }
- .xml-http-intercepter-controller-template * {
- margin: 0;
- padding: 0
- }
- .intercepter-controller-button button {
- position: fixed;
- right: 2vw;
- top: 50%;
- width: 3.8vw;
- height: 3.8vw;
- background: rgb(157, 0, 255);
- border-top: solid .3vw rgb(105, 105, 105);
- border-bottom: solid .3vw rgb(105, 105, 105);
- border-left: dashed .3vw rgb(105, 105, 105);
- border-right: dashed .3vw rgb(105, 105, 105);
- border-radius: 50%;
- outline: none;
- color: rgb(84, 84, 84);
- font-size: .8vw;
- transform: translateY(-50%);
- transition: .5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
- cursor: pointer;
- z-index: 9999
- }
- .controller-content-header {
- padding: 1vw;
- background: var(--bgcolor);
- display: flex;
- justify-content: space-between;
- align-items: center;
- color: white;
- box-shadow: 0 0 1vw #ccc
- }
- .header-right-options {
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: 1vw
- }
- .option-item {
- cursor: pointer
- }
- .intercepter-controller-button:hover button {
- border-top: solid .3vw rgb(157, 0, 255);
- border-bottom: solid .3vw rgb(157, 0, 255);
- border-left: dashed .3vw rgb(157, 0, 255);
- border-right: dashed .3vw rgb(157, 0, 255);
- box-shadow: 0 0 .8vw rgb(157, 0, 255)
- }
- .intercepter-controller-content {
- position: fixed;
- top: 50%;
- left: 50%;
- width: 90vw;
- background: white;
- transition: .3s;
- transform: translate(-50%, -50%) scale(0);
- box-shadow: 0 0 5vw #ccc;
- border-radius: .3vw;
- overflow: hidden;
- z-index: 9999
- }
- .content-show {
- transform: translate(-50%, -50%) scale(1)
- }
- .intercepter-controller-main {
- padding: .5vw 1vw;
- height: 75vh
- }
- .main-header {
- display: flex;
- justify-content: space-between;
- align-items: center
- }
- .main-header-left .main-tab-item, .main-header-right .main-tab-item {
- padding: .5vw;
- background: white;
- border: solid .05vw var(--bgcolor);
- outline: none;
- cursor: pointer;
- font-size: .8vw;
- border-radius: .1vw;
- color: var(--bgcolor);
- transition: .5s
- }
- .main-header-right .main-tab-item:hover,
- .main-header-right .main-tab-item-active,
- .main-header-left .main-tab-item:hover,
- .main-header-left .main-tab-item-active {
- background: var(--bgcolor);
- color: white
- }
- .intercepter-item {
- padding-bottom: 1.5vw;
- height: calc(73.5vh - 1.5vw);
- overflow-y: auto
- }
- .intercepter-item::-webkit-scrollbar{
- appearance: none ; width: .3vw ; background: rgba(255, 255, 255, 0.6)
- }
- .intercepter-item::-webkit-scrollbar-thumb {
- appearance: none ; width: 100% ; background: rgba(161, 161, 161, 0.6) ; border-radius: .15rem
- }
- .intercepter-table {
- margin: 1vw 0;
- width: 100%;
- border-collapse: collapse;
- border-spacing: 0;
- ovflow: hidden
- }
- .intercepter-table .table-header {
- position: sticky;
- top: 0;
- background: rgb(246, 246, 246)
- }
- .intercepter-table .info-message {
- }
- .intercepter-table .info-message tr {
- position: sticky;
- top: 3vw;
- left: 0;
- background: white;
- transition: .4s
- }
- .row-item-remove {
- opacity: 0;
- height: .5vh;
- transform: translateX(-70%)
- }
- .intercepter-table .info-message tr:hover {
- background: linear-gradient(to right, white, var(--bgcolor), white)
- }
- .intercepter-table td {
- padding: 1vw;
- max-width: 6vw;
- white-space: nowrap;
- text-overflow: ellipsis;
- overflow: hidden;
- font-size: .8vw;
- transition: .3s
- }
- .actions-button-group button {
- margin: 0 .2vw;
- background: transparent;
- border: none;
- outline: none;
- cursor: pointer
- }
- .actions-button-group .error {
- color: red
- }
- .actions-button-group .info {
- color: var(--bgcolor)
- }
- .more-about-message {
- background: rgb(246, 246, 246)
- }
- .intercepter-table .more-about-message {
- border: none
- }
- .more-about-message td {
- padding: 0
- }
- .request-message-container {
- max-height: 0vh;
- overflow: hidden;
- display: flex;
- flex-flow: column nowrap;
- justify-content: space-between;
- gap: 1vw
- }
- .more-message-show td {
- padding: 1vw
- }
- .more-message-show .request-message-container {
- max-height: 1000vh
- }
- .request-message-content {
- margin: 1vw 0
- }
- .request-message-form {
- list-style: none;
- display: flex;
- flex-flow: row wrap;
- gap: 2vw
- }
- .request-message-form li {
- margin: .5vw 0;
- flex: auto;
- display: flex;
- justify-content: space-between;
- align-items: center;
- gap: .5vw
- }
- .request-message-form li textarea {
- padding: .5vw;
- width: 100%;
- height: auto;
- border-radius: .2vw;
- border: solid .05vw #ccc;
- outline: none
- }
- .data-list-select, .request-message-form li input {
- padding: .5vw;
- border-radius: .2vw;
- min-width: 3.5vw;
- height: 1.5vh;
- border: solid .05vw #ccc;
- outline: none
- }
- .item-show {
- display: block
- }
- .item-close {
- display: none
- }
- .xml-http-request-more-message-footer button, .xml-http-response-more-message-footer button {
- padding: .5vw 1vw;
- background: var(--bgcolor);
- border: none;
- border-radius: .2vw;
- outline: none;
- color: white;
- cursor: pointer
- }
- </style>
- `
-
- header.insertAdjacentHTML('beforeend', STYLE)

还想看啥啊?没啦,该吃吃该喝喝
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。