更新时间:2022-09-14 14:30:01
阅读目录:
一、 传统的Asp.net页面问题
二、Asp.net MVC中也存在同样的问题
三、使用PRG模式
四、PRG模式在MVC上的实现
一个传统的Asp.net页面的请求会是这样的:
HTTP GET 请求"Register.aspx"
HTTP POST 请求 "Register.aspx"(点击按钮等触发服务器端事件)
数据检验失败, 重新返回到"Register.aspx"
在HTTP POST到"Register.aspx"
数据创建成功, 重新返回到"Register.aspx",提示创建成功
看看好像没有什么问题呀, 但是如果在标记为红色的这步之后,你在浏览器上点击"刷新"按钮, 就会弹出下面的对话框。
这个对话框的意思是, 为了显示你点击"刷新"按钮的页面, 浏览器需要发送你上次提交的数据到服务器端, 之所以会这样的原因是浏览器记录的是上次你的Post请求, 所以你点击"刷新"按钮, 也是重复执行一次Post请求, 而用户其实是想得到初始的页面,也就是GET请求"Register.aspx"页面. 对于大多数不清楚原理的普通用户来说,这样的对话框会让用户会非常困扰.
web系统应当是以URL为标记的资源, 一个URL***代表的一种资源. 当你收藏一个网页,分享一个网页给你朋友的时候, 你用的是网页的URL, 那是因为网页的URL就对应了你想分享的资源.
所以上面方式带来的另外一个问题就是, Get, POST, 以及POST成功后的页面实际上代表了3中不同的资源,但是这三种资源的URL是同一个URL.
假如我们在完成一个注册页面, Controller中的代码是这样的:
// // GET: /Home/ [HttpGet] public ActionResult Register() { return View(); } [HttpPost] public ActionResult Register(Models.RegisterModel registerModel) { return View(); }
View中的代码是:
@using(Html.BeginForm()){ <fieldset> <legend>Register</legend> @Html.ValidationSummary(true) <ol> <li> @Html.LabelFor(m => m.NickName) @Html.TextBoxFor(m => m.NickName) @Html.ValidationMessageFor(m => m.NickName) </li> <li> @Html.LabelFor(m => m.Email) @Html.TextBoxFor(m => m.Email) @Html.ValidationMessageFor(m => m.Email) </li> </ol> <input type="submit" value="Sumbit" /> </fieldset> }
运行以后,当你提交表单的时候,你会发现出现了同样的问题.
PRG模式是Post/Redirect/Get的简称.
当一个Post请求过来的时候, 服务端会处理Post请求后,再发送Redirect(HTTP 303状态码)到浏览器,浏览器之后再发送Get请求到其它页面.
这样做, 浏览器的上一个操作就总是Http Get操作, 而不是Post操作, 也就解决了刷新弹出框的问题.
针对上面的例子,我们的修改思路是:
创建3个不同的Action对应, Post请求到"RegisterProcess"之后,无论成功还是失败, 都会转换成Get请求, 成功转向"RegisterSuccess", 失败转向"Register"
修改之后的Controller代码如下:
// // GET: /Home/ [HttpGet, ImportModelStateFromTempData] public ActionResult Register() { return View(); } [HttpPost, ExportModelStateToTempData] public ActionResult RegisterProcess(Models.RegisterModel registerModel) { if (ModelState.IsValid) { return RedirectToAction("RegisterSuccess"); } return RedirectToAction("Register"); } [HttpGet] public ActionResult RegisterSuccess() { return View(); }
上面的ImportModelStateFromTempData和ExportModelStateToTempData是ActionFilter, 是为了解决Redirect不能保存Model的验证错误的问题.
实现的基本原理是通过ExportModelStateToTempData把Model的验证错误存放到TempData中, 通过ImportModelStateFromTempData从TempData中把验证错误导入.
View代码是:
@using (Html.BeginForm("RegisterProcess", "Home")) { <fieldset> <legend>Register</legend> @Html.ValidationSummary(true) <ol> <li> @Html.LabelFor(m => m.NickName) @Html.TextBoxFor(m => m.NickName) @Html.ValidationMessageFor(m => m.NickName) </li> <li> @Html.LabelFor(m => m.Email) @Html.TextBoxFor(m => m.Email) @Html.ValidationMessageFor(m => m.Email) </li> </ol> <input type="submit" value="Sumbit" /> </fieldset> }
ImportModelStateFromTempData和ExportModelStateToTempData的实现代码如下:
public abstract class ModelStateTempDataTransfer : ActionFilterAttribute { protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName; } public class ExportModelStateToTempData : ModelStateTempDataTransfer { public override void OnActionExecuted(ActionExecutedContext filterContext) { //Only export when ModelState is not valid if (!filterContext.Controller.ViewData.ModelState.IsValid) { //Export if we are redirecting if ((filterContext.Result is RedirectResult) || (filterContext.Result is RedirectToRouteResult)) { filterContext.Controller.TempData[Key] = filterContext.Controller.ViewData.ModelState; } } base.OnActionExecuted(filterContext); } } public class ImportModelStateFromTempData : ModelStateTempDataTransfer { public override void OnActionExecuted(ActionExecutedContext filterContext) { var modelState = filterContext.Controller.TempData[Key] as ModelStateDictionary; if (modelState != null) { //Only Import if we are viewing if (filterContext.Result is ViewResult) { filterContext.Controller.ViewData.ModelState.Merge(modelState); } else { //Otherwise remove it. filterContext.Controller.TempData.Remove(Key); } } base.OnActionExecuted(filterContext); } }
最后,附上本文相关源代码 MVCFormValiation.zip
本文转自JustRun博客园博客,原文链接:http://www.cnblogs.com/JustRun1983/archive/2013/04/18/3027861.html,如需转载请自行联系原作者
IoC容器Autofac(4) - Autofact + Asp.net MVC + EF Code First(附源码)
Entity Framework在Asp.net MVC中的实现One Context Per Request(附源码)
构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(6)-Unity 2.x依赖注入by运行时注入[附源码]
构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(2)-easyui构建前端页面框架[附源码]
[ASP.net MVC] 将HTML转成PDF档案,使用iTextSharp套件的XMLWorkerHelper (附上解决显示中文问题)