MVC_防止HttpPost重复提交
重复提交的场景很常见,可能是当时服务器延迟的原因,如购物车物品叠加,重复提交多个订单。常见的解决方法是提交后把Button在客户端Js禁用,或是用Js禁止后退键等。在ASP.NET MVC 3 Web Application中 如何去防止这类HTTP-Post的重复提交呢? 我们可以借助Session,放置一个Token在View/Page上,然后在Server端去验证是不是同一个Token来判断此次Http-Post是否有效。看下面的代码: 首先定义一个接口,便于扩展。
public interface IPageTokenView
{
/// <summary>
/// Generates the page token.
/// </summary>
string GeneratePageToken(); /// <summary>
/// Gets the get last page token from Form
/// </summary>
string GetLastPageToken { get; } /// <summary>
/// Gets a value indicating whether [tokens match].
/// </summary>
/// <value>
/// <c>true</c> if [tokens match]; otherwise, <c>false</c>.
/// </value>
bool TokensMatch { get; }
}
定义一个Abstract Class,包含一个
public abstract class PageTokenViewBase : IPageTokenView
{
public static readonly string HiddenTokenName = "hiddenToken";
public static readonly string SessionMyToken = "Token"; /// <summary>
/// Generates the page token.
/// </summary>
/// <returns></returns>
public abstract string GeneratePageToken(); /// <summary>
/// Gets the get last page token from Form
/// </summary>
public abstract string GetLastPageToken { get; } /// <summary>
/// Gets a value indicating whether [tokens match].
/// </summary>
/// <value>
/// <c>true</c> if [tokens match]; otherwise, <c>false</c>.
/// </value>
public abstract bool TokensMatch { get; } }
接着是实现SessionPageTokenView类型,记得需要在验证通过后生成新的Token,对于这个Class是把它放到Session中。
public class SessionPageTokenView : PageTokenViewBase
{
#region PageTokenViewBase /// <summary>
/// Generates the page token.
/// </summary>
/// <returns></returns>
public override string GeneratePageToken()
{
if (HttpContext.Current.Session[SessionMyToken] != null)
{
return HttpContext.Current.Session[SessionMyToken].ToString();
}
else
{
var token = GenerateHashToken();
HttpContext.Current.Session[SessionMyToken] = token;
return token;
}
} /// <summary>
/// Gets the get last page token from Form
/// </summary>
public override string GetLastPageToken
{
get
{
return HttpContext.Current.Request.Params[HiddenTokenName];
}
} /// <summary>
/// Gets a value indicating whether [tokens match].
/// </summary>
/// <value>
/// <c>true</c> if [tokens match]; otherwise, <c>false</c>.
/// </value>
public override bool TokensMatch
{
get
{
string formToken = GetLastPageToken;
if (formToken != null)
{
if (formToken.Equals(GeneratePageToken()))
{
//Refresh token
HttpContext.Current.Session[SessionMyToken] = GenerateHashToken();
return true;
}
}
return false;
}
} #endregion #region Private Help Method
/// <summary>
/// Generates the hash token.
/// </summary>
/// <returns></returns>
private string GenerateHashToken()
{
return Utility.Encrypt(
HttpContext.Current.Session.SessionID + DateTime.Now.Ticks.ToString());
}
#endregion
这里有到一个简单的加密方法,你可以实现自己的加密方法.
public static string Encrypt(string plaintext)
{
string cl1 = plaintext;
string pwd = string.Empty;
MD5 md5 = MD5.Create();
byte[] s = md5.ComputeHash(Encoding.Unicode.GetBytes(cl1));
for (int i = 0; i < s.Length; i++)
{
pwd = pwd + s[i].ToString("X");
}
return pwd;
}
我们再来编写一个Attribute继承FilterAttribute, 实现IAuthorizationFilter接口。然后比较Form中Token与Session中是否一致,不一致就Throw Exception. Tips:这里最好使用依赖注入IPageTokenView类型,增加Logging 等机制
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public sealed class ValidateReHttpPostTokenAttribute : FilterAttribute, IAuthorizationFilter
{
public IPageTokenView PageTokenView { get; set; } /// <summary>
/// Initializes a new instance of the <see cref="ValidateReHttpPostTokenAttribute"/> class.
/// </summary>
public ValidateReHttpPostTokenAttribute()
{
//It would be better use DI inject it.
PageTokenView = new SessionPageTokenView();
} /// <summary>
/// Called when authorization is required.
/// </summary>
/// <param name="filterContext">The filter context.</param>
public void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
} if (!PageTokenView.TokensMatch)
{
//log...
throw new Exception("Invaild Http Post!");
} }
}
还需要一个HtmlHelper的扩展方法:
public static HtmlString GenerateVerficationToken(this HtmlHelper htmlhelper)
{
string formValue = Utility.Encrypt(HttpContext.Current.Session.SessionID+DateTime.Now.Ticks.ToString());
HttpContext.Current.Session[PageTokenViewBase.SessionMyToken] = formValue; string fieldName = PageTokenViewBase.HiddenTokenName;
TagBuilder builder = new TagBuilder("input");
builder.Attributes["type"] = "hidden";
builder.Attributes["name"] = fieldName;
builder.Attributes["value"] = formValue;
return new HtmlString(builder.ToString(TagRenderMode.SelfClosing));
}
将输出这类的HtmlString:
<input name="hiddenToken" type="hidden" value="1AB01826F590A1829E65CBD23CCE8D53" />
我们创建一个叫_ViewToken.cshtml的Partial View,这样便于模块化,让我们轻易加入到具体View里,就两行代码,第一行是扩展方法NameSpace
@using Mvc3App.Models;
@Html.GenerateVerficationToken()
假设我们这里有一个简单的Login.cshtml,然后插入其中:
<form method="post" id="form1" action="@Url.Action("Index")">
<p>
@Html.Partial("_ViewToken")
UserName:<input type="text" id="fusername" name="fusername" /><br />
Password:<input type="password" id="fpassword" name="fpassword" />
<input type="submit" value="Sign-in" />
</p>
</form>
这里我们Post的Index Action,看Controller代码,我们在Index上加上ValidateReHttpPostToken的attribute.
[HttpPost]
[ValidateReHttpPostToken]
public ActionResult Index(FormCollection formCollection)
{
return View();
} public ActionResult Login()
{
return View();
}
MVC_防止HttpPost重复提交的更多相关文章
- ASP.NET Web Form和MVC中防止F5刷新引起的重复提交问题
转载 http://www.cnblogs.com/hiteddy/archive/2012/03/29/Prevent_Resubmit_When_Refresh_Reload_In_ASP_NET ...
- webapi 重复提交问题
https://izen.live/Blog/info/13.html /// <summary> /// action方法过滤器 /// </summary> public ...
- 12、Struts2表单重复提交
什么是表单重复提交 表单的重复提交: 若刷新表单页面, 再提交表单不算重复提交. 在不刷新表单页面的前提下: 多次点击提交按钮 已经提交成功, 按 "回退" 之后, 再点击 &qu ...
- 如何解决ajax重复提交的问题
如下一段代码: 先忽略我没引jquery.js的问题,这是一个案例. 当我们点击提交时,控制台输出两次e,在network里查看,可以看到我们的ajax传输了两次,造成了数据重复提交. 一种解释为bu ...
- java防止表单重复提交
用session防止表单重复提交 思路:在服务器端生成一个唯一的随机标识串Token,同时在当前用户的Session域中保存这个Token.然后将Token发送到客户端的Form表单中,在Form表单 ...
- php止刷新页面重复提交
利用session来解决,首先新建一个session,并赋值,第一次提交后改变session的值,当第二次再此提交此内容时,如果不是我们的赋值,就不在处理传过来的数据.如:<?php sessi ...
- java web学习总结(十三) -------------------使用Session防止表单重复提交
在平时开发中,如果网速比较慢的情况下,用户提交表单后,发现服务器半天都没有响应,那么用户可能会以为是自己没有提交表单,就会再点击提交按钮重复提交表单,我们在开发中必须防止表单重复提交. 一.表单重复提 ...
- Spring MVC拦截器+注解方式实现防止表单重复提交
原理:在新建页面中Session保存token随机码,当保存时验证,通过后删除,当再次点击保存时由于服务器端的Session中已经不存在了,所有无法验证通过. 注,如果是集群的方式,则需要将token ...
- Struts2 token禁止重复提交表单
如果服务器响应慢的情况下,用户会重复提交多个表单,这时候有两种设计思想: 1.在客户端使用JS技术,禁止客户重复提交表单.但是这样会使一些不使用浏览器方式登陆的人比如使用底层通信来攻击你的服务器 2. ...
随机推荐
- django母版页的使用
母版页用于处理html页面相同部分内容,避免在不同的页面中重复出现 1.添加母版页 再manage.py文件相同目录下添加templates文件夹用于保存母版页html文件 2.添加母版页Base.h ...
- Android QMI机制
android QMI机制---概论 android QMI机制---QMUX android QMI机制---Qcril初始化流程 android QMI机制---QCRIL消息发送 android ...
- Centos7查询开机启动项服务
问题描述: 最近安装了zabbix设置了一些开机启动服务 例如:zabbix-server.service,httpd.service,mariadb.service,或者系统的firework.se ...
- zabbix疑难之时区问题
zabbix疑难总结: 1.zabbix的web界面的时间不对.晚12个小时整 适用于:安装网上的说法来修改,但是时间仍然不对的情况 我们按照以前的网上的那些方法在配置zabbix,需要配置时区: ...
- 3.7Python数据处理篇之Numpy系列(七)---Numpy的统计函数
目录 目录 前言 (一)函数一览表 (二)统计函数1 (三)统计函数2 目录 前言 具体我们来学Numpy的统计函数 (一)函数一览表 调用方式:np.* .sum(a) 对数组a求和 .mean(a ...
- Static简介
1.static称为静态修饰符,它可以修饰类中得成员.被static修饰的成员被称为静态成员,也成为类成员,而不用static修饰的成员称为实例成员. 2.当 Voluem volu1 = new V ...
- [bug] 验证selenium的显式和隐式等待而发现的一个低级错误
隐式等待:如果在规定时间内网页加载完成,则执行下一步,否则一直等到时间截止,然后执行下一步.按照这说法举了个例子为啥不会按照预期执行了,难不成是这个定义有问题(~~~~~直接否定不是定义的问题,相信它 ...
- golang xorm框架的使用
1.创建engine engine, err := xorm.NewEngine(driverName, dataSourceName) 上述代码创建了一个数据库引擎,可以在一个程序中创建多个engi ...
- .whl文件打开方式 Python
wheel文件本质上就是zip或者rar,只不过他更加方便python的安装以及使用.在之前的图片中我们只要使用pip install wheel 就可以安装wheel. 在安装了wheel之后我们可 ...
- Hue添加MySQL数据库
Hue没有配置RDBMS 问题描述 CHD集群添加完Hue组件之后.使用hive进行查询正常,但是使用DB Query查询报错, 报错内容如下: 解决方法 1. 在CHD集群中点击Hue组件,选择配置 ...