随笔- 23  文章- 0  评论- 0  阅读- 1896 

如何防止表单的重复提交

1.通过js代码,当用户点击提交按钮后,屏蔽提交按钮使用户无法点击提交按钮或点击无效,从而实现防止表单重复提交。【不推荐】通过刷新或者绕过前段页面后仍然能够重复提交表单

<!DOCTYPE HTML>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <script type="text/javascript">
        //默认提交状态为false
        var commitStatus = false;
        function dosubmit(){
            if(commitStatus==false){
                //提交表单后,讲提交状态改为true
                commitStatus = true;
                return true;
            }else{
                return false;
            }
        }
    </script>
</head>

<body>

<script>
    alert(dosubmit());
</script>

<form action="/path/post" onsubmit="return dosubmit()" method="post">
    用户名:<input type="text" name="username">
    <input type="submit" value="提交" id="submit">
</form>
</body>
</html>

2.给数据库增加唯一键约束【不推荐】,重复访问数据库带来额外的负担

alter table tableName_xxx add unique key uniq_xxx(field1, field2)

3.利用Session防止表单重复提交【推荐,但是刷新的话是否会获得一个新的token】

服务器返回表单页面时,会先生成一个subToken保存于session,并把该subToen传给表单页面。当表单提交时会带上subToken,服务器拦截器Interceptor会拦截该请求,拦截器判断session保存的subToken和表单提交subToken是否一致。若不一致或session的subToken为空或表单未携带subToken则不通过。

@RequestMapping("/form")
//开启一个Token
@SubToken(saveToken = true)
public String form() {
  return "/test/form";
}


@RequestMapping(value = "/postForm", method = RequestMethod.POST)
@ResponseBody
//开启Token验证,并且成功之后移除当前Token
@SubToken(removeToken = true)
public String postForm(String userName) {
  System.out.println(System.currentTimeMillis());
  try{
	System.out.println(userName);
    Thread.sleep(1500);//暂停1.5秒后程序继续执行
  }catch (InterruptedException e) {
    e.printStackTrace();
 }
 System.out.println(System.currentTimeMillis());
 return "1";
}

首次提交表单时session的subToken与表单携带的subToken一致走正常流程,然后拦截器内会删除session保存的subToken。当再次提交表单时由于session的subToken为空则不通过。从而实现了防止表单重复提交。

<html>
<head>
<title>Title</title>
</head>
<body>
<form method="post" action="/postForm">
<input type="text" name="userName">
<input type="hidden" name="subToken" value="${subToken}">
<input type="submit" value="提交">
</form>
</body>
</html>

4.使用AOP自定义切入实现

import java.lang.annotation.*;

/**
 * 避免重复提交
 * @author hhz
 * @version
 * @since
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AvoidRepeatableCommit {

    /**
     * 指定时间内不可重复提交,单位毫秒
     * @return
     */
    long timeout()  default 30000 ;

}

自定义切入点

@Aspect
@Component
public class AvoidRepeatableCommitAspect {

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * @param point
     */
    @Around("@annotation(com.xwolf.boot.annotation.AvoidRepeatableCommit)")
    public Object around(ProceedingJoinPoint point) throws Throwable {

        HttpServletRequest request  = ((ServletRequestAttributes)RequestContextHolder.currentRequestAttributes()).getRequest();
        String ip = IPUtil.getIP(request);
        //获取注解
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        //目标类、方法
        String className = method.getDeclaringClass().getName();
        String name = method.getName();
        String ipKey = String.format("%s#%s",className,name);
        int hashCode = Math.abs(ipKey.hashCode());
        String key = String.format("%s_%d",ip,hashCode);
        log.info("ipKey={},hashCode={},key={}",ipKey,hashCode,key);
        AvoidRepeatableCommit avoidRepeatableCommit =  method.getAnnotation(AvoidRepeatableCommit.class);
        long timeout = avoidRepeatableCommit.timeout();
        if (timeout < 0){
                        //过期时间5分钟
            timeout = 60*5;
        }
        String value = (String) redisTemplate.opsForValue().get(key);
        if (StringUtils.isNotBlank(value)){
            return "请勿重复提交";
        }
        redisTemplate.opsForValue().set(key, UUIDUtil.uuid(),timeout,TimeUnit.MILLISECONDS);
        //执行方法
        Object object = point.proceed();
        return object;
    }

}

CSS选择器

通配符选择器:用*来表示,可以选择所有元素

*{marigin: 0;
 padding: 0;
 font-size: 14px;
}

类选择器 :类选择器根据类名来选择,前面以”.”来标志,是以一独立于文档元素的方式来指定样式

 .demo {
 width: 200px;
 height: 200px;
 margin: 50px auto;
 background: #2DC4CB;
 }

元素选择器:元素选择器(标签名选择器),是css3选择器中最常见而且最基本的选择器。元素选择器其实就是文档的元素,如html,body,p,div等等下下面例子中选择了span元素,并设置了字体颜色为红色。

ID选择器:我们在类使用时是在相对应的类名前加上一个“.”号

 #demo {
 width: 200px;
 height: 200px;
 margin: 50px auto;
 background: #FF0000;
 }

HTML常用标签:

1.段落、标题 <p></p>	<h1></h1>...<h6></h6>
用来定义网页中的一段文本,段落与段落之间换行。
属性:align:定义段落中的文本水平方向的对齐方式。
属性值:left左对齐、right右对齐、center居中对齐

2.换行标签: <br/>
指行与行之间换行,他是一个单标签。

3.<blockquote>文字段落缩进</blockquote>

4.列表:无需ul、有序ol
<ul type="circle"> 
         <li>列表项</li> 
         <li>列表项</li>
</ul>circle空心圆、disc实心圆、square实心方块

5.特殊样式
<b>加粗</b>
<i>倾斜</i>
<u>下划线</u>
<s>删除线</s>
<big>放大</big>
<small>缩小</small>
<strong>加强强调</strong>
<em>强调倾斜</em>

6.<img/>图片标签
属性:
src:指定图片源文件;
alt :图片未加载成功的提示文字;
width:指定图片的宽度;
height:指定图片的高度;
border:指定图片的边框样式;
alghr:top/bottom/middle;图片位于两端文字在垂直方向的一个上/中/下的对齐方式;

7.超链接<a></a>
属性:
href:连接地址;空连接可以用"#"代替;
target :_self/_blank;打开方式;_self在当前页面中打开,_blank在新的空白窗口打开

8.<div></div>可定义文档中的分区或节
<span></span> 标签被用来组合文档中的行内元素


表格设置与样式:
<table></table>:表格基本结构

<table>
   <caption>表格标题</caption>
  	<tr>
       	    <td>我是第一行的第一个单元格</td>
            <td>我是第一行的第二个单元格</td>
    	</tr>
   	<tr>
       	    <td>我是第二行的第一个单元格</td>
       	    <td>我是第二行的第二个单元格</td>
    	</tr>
</table>

table的属性:width:表格的宽度 height:表格的高度 align:表格的对齐方式
tr的属性:height:行的高度 bordercolor:边框的颜色

表单内置部件:

单选按钮 (checked默认)

<input type="radio" name=" " checked />  
<input type="radio" name=" " />

复选按钮 (checked默认)

<input type="checkbox" name=" " checked />  
<input type="checkbox" name=" " />

列表框

<select size="一次显示多少个" multiple="可多选">
<option selected>默认的下拉菜单1</option>
<option>下拉菜单2</option>
</select>

单行文本框

<input type="text" size="文本框显示的长度" maxlength="最长字符数"/>

文件域

<input type="file"/>
 posted on   春秋流千事  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示