目前我知道的有 3 种 1:按钮绑定一个变量绑定 disable,请求完成后改为 false (每个按钮都要加感觉有点蠢) 2:ajax 之前 全局加 loading 弹层, 防止重复点击(如果时间很短,loading 也影响体验) 3:Vue 中 弄个指令,给一个时间,该时间内只执行一次事件(这个时间感觉也不靠谱,太长太短都不好)
1
alexmy 2019-09-05 10:23:37 +08:00 3
lodash 库的 throttle, debounce,我的个人小站就用这个。
|
2
dmjob2015222 2019-09-05 10:25:18 +08:00
防抖、节流
|
3
flyingfz 2019-09-05 10:26:17 +08:00 6
http 接口 的幂等性的要求 , 了解一下 。
|
4
GzhiYi 2019-09-05 10:26:38 +08:00
节流一把梭
|
5
otakustay 2019-09-05 10:29:39 +08:00
你可以考虑做个 Button,onClick 允许返回 Promise 自己处理 disabled
|
6
LyleRockkk OP @alexmy 刚刚看了一下这两个 func, 都是要给一个 wait 时间的,跟我说的方法 3 一个道理,我意思就是这个时间不太好给,太长太短都不行,最好能对于接口响应的时间
|
7
Variazioni 2019-09-05 10:34:32 +08:00 1
只有幂等的接口才能完美解决这个问题。。。
|
8
hlwjia 2019-09-05 10:34:35 +08:00 via iPhone
2 就可以,体验那里你多虑了。
除非你是亚马逊那种页面加载慢一点都损失几个 million 收入的公司 |
9
roscoecheung1993 2019-09-05 10:39:24 +08:00
方案一可以的,按钮做个统一封装就好了,毕竟也不是所有按钮都需要加防重提交是吧
|
10
murmur 2019-09-05 10:42:51 +08:00
disable 是一定要加的
|
11
learnshare 2019-09-05 10:43:03 +08:00 2
“每个按钮都要加感觉有点蠢” 写业务逻辑不就是这样么
方案 2 3 更蠢好么 |
12
LyleRockkk OP @learnshare 先把需要的按钮单独处理再说,之后再研究全局的方式,哈哈
|
13
cmdOptionKana 2019-09-05 10:49:54 +08:00
加 disable 的好处是,可以有视觉效果让用户明白正在处理中。否则用户不知道究竟点上没有,会烦躁地多点几下。
|
14
lihongjie0209 2019-09-05 10:51:11 +08:00
@flyingfz #3 说的简单, 前端一个 disable 的事情后端复杂的要死
|
15
Augi 2019-09-05 10:53:09 +08:00
我觉得 可以用方案一,disable 可以以 loading 的形式呈现,现在好多成熟的组件库也都有 按钮的 loading 状态,可以统一封装下。
|
17
Greesea 2019-09-05 10:56:20 +08:00
表单一次性 token
|
18
zppass 2019-09-05 11:04:42 +08:00
写个组件吧,用的时候调用一下就可以。
有的云服务,提交 API 请求会有一个时间设置,防止时间间隔内重复提交,阿里云那个在 API 选项,放置重复攻击。 |
19
ChefIsAwesome 2019-09-05 11:10:29 +08:00 via Android
1.用户按回车照样提交。
2.提交成功了或者失败了才有可能让用户再次提交,不是什么 debounce 之类的。 要 disable 也是 disable 那个 form。你那 123 都不靠谱。任何请求都是这三个状态:pending,resolved,reject。遮罩或者按钮怎么显示,是这个 form 不同状态下的不同 ui。 |
20
acthtml 2019-09-05 11:12:24 +08:00
方案 1 最佳
|
22
jowan 2019-09-05 11:14:52 +08:00
你没发现很多应用 点击按钮后 按钮就变成半透明或者灰色不可点击状态 并且变成 loading 了吗
其实就 1 方案的改良版 不管你用什么方法 你封装成组件就行了 1 2 3 都行 可以参考 vue 的各大框架的按钮 基本都有个 loading 属性 |
23
LyleRockkk OP @ChefIsAwesome 没用 form 表单形式提交,直接 function 封装 ajax 走的后台接口
|
24
arslion 2019-09-05 11:24:17 +08:00 1
通过使用加 disabled 等控制界面的方式来保障交互体验
但逻辑才是最必要的,在前端使用 debounce 和 loading flag,在后端实现幂等接口 |
25
toma77 2019-09-05 11:25:14 +08:00
方法 2
|
26
Woodywuuu 2019-09-05 11:26:58 +08:00
前后端都得搞。
提个前端思路,用 XHR 的 abort 方法。在路由库里面做判断,可以选择性中断不想要的请求。 想了下应用场景: 1. 切换路由时将前一个请求 abort。 2. body 相同的 post 请求,在前一个还在执行的时候,不允许后续提交。 3. ....暂时没想到 目前线上用的是 axios 实现的,效果还行。 |
27
abel1989 2019-09-05 11:30:14 +08:00
自己用 VUE 封装一个 BUTTON 的组件
|
28
Vegetable 2019-09-05 11:38:55 +08:00
不封装通用功能才是蠢哦
|
29
Raymon111111 2019-09-05 11:49:44 +08:00
这种按钮组件不应该是封装好的吗?
前端做了之后能拦住大部分 然后后端再把那种恶意发请求的拦住就行了 |
30
xrr2016 2019-09-05 11:56:00 +08:00 via Android
让后端加 redis 缓存啊,每个请求一个 key,有重复就报错
|
31
dartabe 2019-09-05 11:57:26 +08:00
vue 和 react 都能封装各种按钮吧
|
32
molvqingtai 2019-09-05 12:00:38 +08:00 via Android
@LiuJiang lodash 支持按需加载啊
|
33
zhuweiyou 2019-09-05 12:03:44 +08:00
用 disable 的方式。
评论说 防抖、节流的,这只能控制点击的频率 /频次。 经常会有这种场景,比如: 点击支付。 你搞个防抖是不对的,因为我这订单就只能付一次,我多点几下就不对了。 |
34
ChefIsAwesome 2019-09-05 12:27:05 +08:00
@LyleRockkk 那就是这个 button 得有那三个状态,根据不同状态来显示不同的东西。而且 button 光显示遮罩也是不行的。用户点了一次之后,按到空格照样会造成点击。你必须得根据请求的状态,html 里 disable 或者在 onClick 里头 return 掉。如果你是个比较复杂的大程序,把 api 请求这层分出来了,为了保险起见,也应该在 api 请求那里设置 flag,请求没完成之前不能再发请求。
|
35
keelii 2019-09-05 12:51:57 +08:00
jQuery 中有个封装事件叫做 one 实际上就是事件处罚完了立即解绑。完事了你可以按需求再绑回去。
|
36
BOYPT 2019-09-05 12:55:39 +08:00
没 vue 经验,但在 angular 里面我用 ng-disable 绑定一个 ajax 过程的配置的 scope 变量,也就是相当于方法 1 吧,ajax 过程我做了封装,所有按钮只需要设置那一个参数。
|
37
kisshere 2019-09-05 13:14:56 +08:00
个人倾向于 disable
|
40
qiaobeier 2019-09-05 13:34:58 +08:00
所谓异步编程。ajax 的几个阶段都做成全局的 events 算了。
|
41
simonv3ex 2019-09-05 13:55:36 +08:00
你让前端的所有请求都走一个通道,disable 的开关就在那控制,再封一个 Buttom 或其他数据相关的组件,里面的 disable 就连这个通道的 disable
|
42
bhaltair 2019-09-05 14:08:18 +08:00
1 点击 debounce
2 axja 拦截重复请求 目前做到了 1 |
43
flashback313 2019-09-05 14:10:01 +08:00
disable 其实并没有什么问题,另外可以尝试全局 loading 就是蒙层那种,一旦发出请求就弹出
|
44
abelmakihara 2019-09-05 15:04:32 +08:00
这几种我还是喜欢用 loading 其次 lodash 的
|
45
s247769541 2019-09-05 15:10:18 +08:00
封装组件,用一个 disabled 属性控制下面所有表单元素的 disabled 属性。。。 参考 element-ui 的
|
46
px920906 2019-09-05 15:32:44 +08:00
按钮 disable 掉+loading 动画就挺好。
原生 js 或者 jquery,可以封装在 ajax 库里,比如叫 btnGet,把按钮元素作为参数传进去 vue 的话,在 data 里加一个 loading 对象,属性初始值都为 false,发起请求前 loading.ajaxCall1 = true, 成功或失败后 loading.ajaxCall = false。 另外,axios 有个 cancel 的功能 -> https://github.com/axios/axios#cancellation 用这个给项目加了取消重复请求的功能 ,目前看来还不错。 |
47
quanjw 2019-09-05 15:47:41 +08:00
$("#submit").attr({"disabled":"disabled"});
$("#submit").removeAttr("disabled"); |
48
walhu 2019-09-05 15:54:37 +08:00
加一个验证码机制。每次访问之后后段刷新。这样就不怕了
|
49
Melting 2019-09-05 16:00:41 +08:00
之前为了偷懒,给请求做一个 lrucache,短时间的非 get 又是同样的请求,可以取消,用 axios.cancelToken 很好实现
|
50
g0thic 2019-09-05 16:02:43 +08:00
2 就可以了,如果你不喜欢页面 loading,就封装个按钮组件,按钮加 loading 状态
|
52
xianxiaobo 2019-09-05 16:21:43 +08:00
v 站很多时候我好像只点击了一次,但是说我回复和上一次相同,就给我取消了.
|
53
jevirs 2019-09-05 16:27:07 +08:00
我有个想法: 利用 request 和 response 的拦截器,每一个接口对应一个状态,request 出去的时候在全局的 map 中将对应的接口状态改为 pending,新的 request 进来,如果是 pengding 状态就直接返回了;
response 回来之后,再把对应的状态改为 done,这样就可以就收新的 request 出去; 不确定 reponse 拦截器里是否能找到对应的 request... |
54
chen2019 2019-09-05 16:30:13 +08:00
组成 [md5(data)]=有效时间 然后请求前判断 key 值为 md5(data)是否存在 结合有效时间 决定是否需要阻止这个请求就可以了
|
55
yc8332 2019-09-05 16:35:54 +08:00
前端应该就是加个变量锁住吧。。后端也是要锁住,redis increment 可以解决
|
56
zhixuanziben 2019-09-05 16:48:12 +08:00
@LiuJiang lodash 支持按需加载,只引自己想要的就行了
|
57
winiex 2019-09-05 16:54:18 +08:00
理解 debounce 的流程就好。如果不想引入库的话自己用 setTimeout 写一个也很简单。
|
58
lizz666 2019-09-05 17:36:35 +08:00
节流,最好能自己写
|
59
zhazi 2019-09-05 17:51:49 +08:00
etag
|
60
zongwan 2019-09-05 19:37:44 +08:00
不就是验证码吗。。。
|
61
TomVista 2019-09-05 19:55:21 +08:00 via iPhone
我用的是异步函数自我同步。
用闭包保留一个状态,isDoing-true,回调或者 await 之后,改 isDoing-false, 这样,在执行队列中只能有一个该函数 |
62
RubyJack 2019-09-05 20:38:52 +08:00
幂等接口真是笑死我了,button 引入点击状态是真的难, 只能甩锅给后端了
加购物车这种场景, 谁来示范一下幂等? |
63
redbuck 2019-09-05 21:36:37 +08:00
方案一改一改。
接口有统一封装就改请求函数,没有就劫持 ajax。 用链接和参数做 key,请求就标记,回来就干掉。 |
64
lihongjie0209 2019-09-05 21:40:14 +08:00
@RubyJack #62 有些人认为接口幂等很简单, 没办法
|
65
jss 2019-09-05 22:01:47 +08:00 via iPhone
就不能给按钮一个 loading ?
|
66
wispx 2019-09-05 22:15:14 +08:00 via iPhone
表单令牌了解一下
|
67
wupher 2019-09-06 09:25:26 +08:00
抛开前端不提,后端也要防重放攻击啊。
人家不通过前端,直接跑个脚本发 http 请求攻击肿么办? |
68
lihongjie0209 2019-09-06 09:52:01 +08:00
@wupher #67 攻击==表单重复提交??
|
69
wupher 2019-09-06 10:11:53 +08:00
@lihongjie0209 重放攻击是这样。如果你某个业务需要特别多的计算,而且结果无法实现幂等,那么攻击者可以通过录制请求或者伪造请求。大量发往服务器,实现攻击目的。
|
70
lihongjie0209 2019-09-06 10:28:03 +08:00
@wupher #69
用户提交表单的时候我们默认用户处于一个安全的状态, 比如登录,验证码, 表单签名。 攻击者处于我们系统的最外围, 要攻击必须先突破我们的安全限制才能进行下一步的动作。 你把表单重复提交这种安全域范围内的事情当作攻击, 就相当于你为了预防 1000KM 外的狙击手每天呆在地下堡垒 |
71
wupher 2019-09-06 11:17:36 +08:00
@lihongjie0209 这个看业务,看团队,看技术了。我相信表单和表单也不一样,对吧?
个人觉得,像支付宝付款那个表单,怎么防范都是应该的,对吧。 防重放我觉得其实也没你想的那么难,有很多简单的策略和实现就能初步实现。实现后通过诸如 AOP、API Gateway、Filter 都是简单配置一下即可。并不会对业务开发造成太高代价。 |
72
lihongjie0209 2019-09-06 11:32:18 +08:00
@wupher #71
重放不难防范, 但这个不是我们是使用一项技术的原因。一大堆简单的技术放在一起复杂度还是会大量的增加 |
73
lihongjie0209 2019-09-06 11:33:46 +08:00
@wupher #71 至于你说的某些特别重要的表单, 那么应该是针对几个表单的接口做安全处理, 而不是全局性的
|
74
RV0n 2019-09-06 13:57:01 +08:00
加锁
|
75
mazai 2019-09-06 14:30:37 +08:00
加个 loading 防护罩
|
76
source 2019-09-09 16:19:54 +08:00
前端这边可以写个工具方法,需要防重复提交的 api 用它包装一下(假如它返回一个 promise )
// mock api function api(params) { return new Promise(function(resolve, reject) { setTimeout(() => resolve(params), 5000) }); } // 生成带锁的 api function lockApi(api) { let lock = false return (...params) => { if (!lock) { lock = true return api(...params) .then(data => { lock = false return data }) } else { return Promise.reject('too frequent!') } } } |
77
source 2019-09-09 16:21:13 +08:00
缩进炸了,贴张截图吧
|