Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 582|回复: 0

注册登录过程点滴(三):解决MVC3中使用Ajax.BeginForm会重复提交数据的问题

[复制链接]
  • TA的每日心情
    奋斗
    3 天前
  • 签到天数: 802 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    72万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    726006
    发表于 2021-6-22 23:47:29 | 显示全部楼层 |阅读模式

    MVC3这个开发框架还是很受大家欢迎的,我个人也非常喜欢,逢人总要夸奖一番,虽然还有很多地方需要完善,但总体上来说很棒!

    不过,最近碰到一个比较纠结的问题,在点击注册时,为了使用一些友好的前端交互效果,所以使用 Ajax.BeginForm的方式,代码如下:

    @using (Ajax.BeginForm( " Register "" Account "new AjaxOptions { UpdateTargetId =  " ResultPannel ", OnBegin =  " RegOngoing ", OnSuccess =  " RegDone " }))
    {...

    分别通过 RegOngoing和 RegDone 执行注册过程中和完成后的前端提示效果。

    但这种方式经常会在数据库中出现重复的数据,分析了一下原因,初步判断应该是注册事件被重复提交了,虽然在后端逻辑层会检测数据是否存在,但其执行过程中本质上是会顺序执行各种逻辑,所以在前一条还没有插入数据库,后一天已经被提交过来被检测的概率是存在的,尤其是如果在数据访问层端有较长的事务性逻辑存在的话,那这种重复提交的概率会更高!

     

    采取的第一套方法:就是在用户点击后将注册按钮disable掉,并给与友好提示,避免用户去重复点击注册...

    function RegOngoing() {
       $( " #Register ").attr( " disabled "true);
       $( " #Register ").val( " 信息提交中... ");}

     这在很长一段时间内我们都认为解决了这个问题,可是在月黑分高的一个晚上,重复数据又出现了,这下纠结了,难道是被黑了?

    利用一切可利用的资源,google、baidu、博客园、qq群等等到处找问题根源,都没有很好的结果。最近团队开会,仔细的分析了一下,发觉问题可能还是出在上面分析过的可能性,比如:很多用户有双击的习惯,很多用户有连续按回答的习惯...,只要RegOngoing反应迟钝一点,就还是会存在这种被重复提交注册的概率,虽然概率很小,但问题不能放过去,针对此种可能性,我们对这个注册场景进行了无思考时间的压力测试,发觉果真还是会被重复提交。

     

    这样,第二套方案应运而生: 采用token预防机制,在Register的action中增加一个基于IP的token策略,并在注册逻辑执行完成后删除token(这里不管是否成功,都必须删除哦,不然你再想注册就有可能不行)。


    [HttpPost]

    public ActionResult Register(RegisterModel reg)
            {
                //防止用户并发提交数据
                string key = StringHelper.GetClientIP();
                if (!string.IsNullOrEmpty(RegTokenHelper.findTokenByKey(key)))
                {
                    return Content("errorSubmit");
                }
                //生成用户Token
                RegTokenHelper.generateToken(key);

                if (IsValidateCode(reg.sCode) == false)
                {
                    //删除Token
                    RegTokenHelper.deleteToken(key);
                    return Content("errorCode");
                }

                ...
                            
                //删除Token
                RegTokenHelper.deleteToken(key);
                return Content(url);
            }

     这里主要是用本机的IP作为本机唯一访问的标识,至于token值可以自己生成,也可以用系统的,我们采用的是guid值。

     

    至此,这个问题基本上可以告一段落了!但是其实不知道大家发现没有,上述方法中还存在一定的问题,或者注册失败的风险,就是当用户在同一个局域网内并发的进行注册,那是有可能失败的,因为其token是相同的,后者会被认为是已经正在注册!

    这种场景其实也是有解决方法,就是不要使用IP作为key,而采用session。根据我们网站的用户特点,前者发生的概率实在是微乎其微,而且本质上没有什么危害性,所以我们没有采用session,而默认接受这种风险了^_^

     至于大家要追求完美,那就无可非议了! 同时希望MVC的团队能从框架性上去解决这个问题,因为这种场景是在是太多啦!!!

    如果大牛们还有更好的办法,Q我或者留言给我吧 

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-11-14 16:02 , Processed in 0.142722 second(s), 27 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表