Asp.net MVC中防止HttpPost重复提交
2016-03-21 16:09
477 查看
重复提交的场景很常见,可能是当时服务器延迟的原因,如购物车物品叠加,重复提交多个订单。常见的解决方法是提交后把Button在客户端Js禁用,或是用Js禁止后退键等。在ASP.NET
MVC 3 Web Application中 如何去防止这类HTTP-Post的重复提交呢? 我们可以借助Session,放置一个Token在View/Page上,然后在Server端去验证是不是同一个Token来判断此次Http-Post是否有效。看下面的代码:
首先定义一个接口,便于扩展。
定义一个Abstract Class,包含一个
接着是实现SessionPageTokenView类型,记得需要在验证通过后生成新的Token,对于这个Class是把它放到Session中。
这里有到一个简单的加密方法,你可以实现自己的加密方法.
我们再来编写一个Attribute继承FilterAttribute,
实现IAuthorizationFilter接口。然后比较Form中Token与Session中是否一致,不一致就Throw
Exception. Tips:这里最好使用依赖注入IPageTokenView类型,增加Logging 等机制
还需要一个HtmlHelper的扩展方法:
将输出这类的HtmlString:
我们创建一个叫_ViewToken.cshtml的Partial View,这样便于模块化,让我们轻易加入到具体View里,就两行代码,第一行是扩展方法NameSpace
点击Retry后,这时就会出现预期Exception,这里只是为了演示,实际中可能需要记录日志,做异常处理。
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Exception: Invaild Http Post!
有兴趣您可以自己试一下,希望对您Web开发有帮助。
原文出自:/article/4752462.html
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(); }
好的,完了,由于篇幅有限,单元测试代码不贴了。让我们运行程序在IE中. 正常点击Button后提交表单,此时按F5再次提交,看到这个提示框:
点击Retry后,这时就会出现预期Exception,这里只是为了演示,实际中可能需要记录日志,做异常处理。
Invaild Http Post!
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.Exception Details: System.Exception: Invaild Http Post!
有兴趣您可以自己试一下,希望对您Web开发有帮助。
原文出自:/article/4752462.html
相关文章推荐
- 转:百度手机地图网络性能优化实践
- 计算机网络体系结构(一)
- 破坏网络可信身份认证,黑灰产业链正在兴起
- 加速度计的理解 http://www.geek-workshop.com/forum.php?mod=viewthread&tid=1695&highlight=%D6%B8%C4%CF
- Android网络编程的Socket通信总结
- Android开发-网络通信2
- Android开发-网络通信1
- Linux网络编程--epoll模型ET触发模式之epolloneshot事件
- 转,关于TCP粘包问题的一些思路
- 关于Http 500 No OutPut Folder的错误
- HTTP报文
- 计算机网络学习笔记——计算机网络的概述(2)
- iOS网络编程之AFNetworking的使用
- iOS中https的证书验证
- IOS网络开发NSURLSession详解(一)概述
- 互联网的网络地址分为A~E五类
- 破坏网络可信身份认证,黑灰产业链正在兴起
- 大型网站架构学习笔记(转自http://www.cnblogs.com/xrq730/p/4954152.html)
- [疯狂Java]网络:URL编解码(URLDecoder、URLEncoder)
- 数据库视图介绍(一)(http://www.cnblogs.com/pony/archive/2008/07/15/1243210.html)