Asp.net MVC - 使用PRG模式(附源码)

阅读目录:html

1、 传统的Asp.net页面问题web

2、Asp.net MVC中也存在一样的问题浏览器

3、使用PRG模式服务器

4、PRG模式在MVC上的实现ide

 

一. 传统的Asp.net页面问题

一个传统的Asp.net页面的请求会是这样的:
    HTTP GET 请求"Register.aspx"
    HTTP POST 请求 "Register.aspx"(点击按钮等触发服务器端事件)
    数据检验失败, 从新返回到"Register.aspx"
    在HTTP POST到"Register.aspx"
    数据建立成功, 从新返回到"Register.aspx",提示建立成功
 
看看好像没有什么问题呀, 可是若是在标记为红色的这步以后,你在浏览器上点击"刷新"按钮, 就会弹出下面的对话框。post


 
这个对话框的意思是, 为了显示你点击"刷新"按钮的页面, 浏览器须要发送你上次提交的数据到服务器端, 之因此会这样的缘由是浏览器记录的是上次你的Post请求, 因此你点击"刷新"按钮, 也是重复执行一次Post请求, 而用户实际上是想获得初始的页面,也就是GET请求"Register.aspx"页面. 对于大多数不清楚原理的普通用户来讲,这样的对话框会让用户会很是困扰.
 
web系统应当是以URL为标记的资源, 一个URL最好表明的一种资源. 当你收藏一个网页,分享一个网页给你朋友的时候, 你用的是网页的URL, 那是由于网页的URL就对应了你想分享的资源.
因此上面方式带来的另一个问题就是, Get, POST, 以及POST成功后的页面实际上表明了3中不一样的资源,可是这三种资源的URL是同一个URL.
 url

二. Asp.net MVC中也存在一样的问题

假如咱们在完成一个注册页面, Controller中的代码是这样的:
 spa

复制代码
 //
        // GET: /Home/
        [HttpGet]
        public ActionResult Register()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Register(Models.RegisterModel registerModel)
        {
            return View();
        }
复制代码

View中的代码是:.net

复制代码
@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>
}
复制代码

运行之后,当你提交表单的时候,你会发现出现了一样的问题.
 code

三. 使用PRG模式

PRG模式是Post/Redirect/Get的简称.
当一个Post请求过来的时候, 服务端会处理Post请求后,再发送Redirect(HTTP 303状态码)到浏览器,浏览器以后再发送Get请求到其它页面.
这样作, 浏览器的上一个操做就老是Http Get操做, 而不是Post操做, 也就解决了刷新弹出框的问题.


 

四. PRG模式在MVC上的实现

针对上面的例子,咱们的修改思路是:
建立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

Asp.net MVC - 使用PRG模式(附源码)

相关文章
相关标签/搜索