有条件的ModelState合并 [英] Conditional ModelState Merge

查看:227
本文介绍了有条件的ModelState合并的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我实现了<一个第二反应href=\"http://stackoverflow.com/questions/4642845/asp-net-mvc-how-to-$p$pserve-modelstate-errors-across-redirecttoaction\">$p$pserve在整个的ModelState RedirectToAction?问题的错误,其中包括使用两个自定义ActionFilterAttributes。我喜欢的解决方案,它使code只需添加一个属性需要该功能的方法清洁。

该解决方案适用于大多数情况下,但我已经穿过一个问题具有重复局部视图运行。基本上,我有一个使用它自己的模型局部视图,从父视图使用模型分开。

我的code从主视图的简化版本:

  @for(INT I = 0; I&LT; Model.Addresses.Count;我++)
{
        地址=(地址)Model.Addresses [I]
        @ Html.Partial(_ AddressModal,地址);
}

局部视图_AddressModal

  @model Acme.Domain.Models.Address
[...]
@ Html.TextBoxFor(型号=&GT; model.Address1,新{@class =表格控})
[...]

在不使用自定义ActionFilterAttributes一切正常。随着局部视图的每次执行时,兰巴前pressions这样的模型=> model.Address1拉从ModelState中正确的值。

问题是,当我得到重定向并使用的自定义ActionFilterAttributes。核心问题是,不仅是更新地址的一个实例的ModelState,而是由局部视图建立的所有地址的ModelState中被覆盖,使它们包含的不是正确的实例值相同的值。

我的问题是我怎么修改自定义ActionFilterAttributes,使其仅更新一个受影响的地址实例的ModelState中,不是所有的ModelStates?我想避免添加任何使用该属性的方法,保持清洁的实现。

下面是其他问题自定义ActionFilterAttributes code:

 公共类SetTempDataModelStateAttribute:ActionFilterAttribute
{
    公共覆盖无效OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);
        filterContext.Controller.TempData [的ModelState] =
           filterContext.Controller.ViewData.ModelState;
    }
}公共类RestoreModelStateFromTempDataAttribute:ActionFilterAttribute
{
    公共覆盖无效OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        如果(filterContext.Controller.TempData.ContainsKey(ModelState中))
        {
            filterContext.Controller.ViewData.ModelState.Merge(
                (ModelStateDictionary)filterContext.Controller.TempData [的ModelState]);
        }
    }
}


解决方案

如果的这个实现(福斯特)不工作:
我用它在很大程度上和从未有过的一个问题。

您正确设置属性? RestoreModelStateFromTempDataAttribute GET 动作和 SetTempDataModelState 操作?

下面是所需要的4类(出口,进口,转让和验证)的ModelState

  [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,的AllowMultiple = FALSE,继承= TRUE)]
    公共类ExportModelStateToTempDataAttribute:ModelStateTempDataTransfer
    {
        公共覆盖无效OnActionExecuted(ActionExecutedContext filterContext)
        {
            //只有当复制是的ModelState无效,我们正在执行一个重定向(即PRG)
            如果(filterContext.Controller.ViewData.ModelState.IsValid&安培;!&安培;
                (filterContext.Result是RedirectResult || filterContext.Result是RedirectToRouteResult))
            {
                ExportModelStateToTempData(filterContext);
            }            base.OnActionExecuted(filterContext);
        }
    }
 ///&LT;总结&gt;
    ///一个行为过滤器从TempData的进口的ModelState。
    ///你需要这个来装饰你的GET操作使用的&lt时,见CREF =ValidateModelStateAttribute/取代。
    ///&LT; /总结&gt;
    ///&LT;&言论GT;
    ///有用继PRG时(邮政,重定向,获取)的模式。
    ///&LT; /言论&GT;
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,的AllowMultiple = FALSE,继承= TRUE)]
    公共类ImportModelStateFromTempDataAttribute:ModelStateTempDataTransfer
    {
        公共覆盖无效OnActionExecuted(ActionExecutedContext filterContext)
        {
            如果我们渲染视图/部分//仅从TempData的复制
            如果(filterContext.Result是的ViewResult)
            {
                ImportModelStateFromTempData(filterContext);
            }
            其他
            {
                // 去掉它
                RemoveModelStateFromTempData(filterContext);
            }            base.OnActionExecuted(filterContext);
        }
    } [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,的AllowMultiple = FALSE,继承= TRUE)]
    公共抽象类ModelStateTempDataTransfer:ActionFilterAttribute
    {
        保护静态只读字符串键= typeof运算(ModelStateTempDataTransfer).FullName;        ///&LT;总结&gt;
        ///导出当前的ModelState到TempData的(上接收费)。
        ///&LT; /总结&gt;
        保护静态无效ExportModelStateToTempData(ControllerContext上下文)
        {
            context.Controller.TempData [关键] = context.Controller.ViewData.ModelState;
        }        ///&LT;总结&gt;
        ///填充与TempData的值当前的ModelState
        ///&LT; /总结&gt;
        保护静态无效ImportModelStateFromTempData(ControllerContext上下文)
        {
            VAR prevModelState = context.Controller.TempData [关键]作为ModelStateDictionary;
            context.Controller.ViewData.ModelState.Merge(prevModelState);
        }        ///&LT;总结&gt;
        ///删除的ModelState从TempData的
        ///&LT; /总结&gt;
        保护静态无效RemoveModelStateFromTempData(ControllerContext上下文)
        {
            context.Controller.TempData [关键] = NULL;
        }
    }  ///&LT;总结&gt;
    ///为一个控制器动作之前,自动验证的ModelState一个ActionFilter被执行。
    ///执行重定向如果的ModelState是无效的。假设在&lt;见CREF =ImportModelStateFromTempDataAttribute/&GT;用在在GET操作。
    ///&LT; /总结&gt;
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method,的AllowMultiple = FALSE,继承= TRUE)]
    公共类ValidateModelStateAttribute:ModelStateTempDataTransfer
    {
        公共覆盖无效OnActionExecuting(ActionExecutingContext filterContext)
        {
            如果(!filterContext.Controller.ViewData.ModelState.IsValid)
            {
                如果(filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    ProcessAjax(filterContext);
                }
                其他
                {
                    ProcessNormal(filterContext);
                }
            }            base.OnActionExecuting(filterContext);
        }        受保护的虚拟无效ProcessNormal(ActionExecutingContext filterContext)
        {
            //出口的ModelState到TempData的所以它可以在下次请求
            ExportModelStateToTempData(filterContext);            //重定向到付诸行动
            filterContext.Result =新RedirectToRouteResult(filterContext.RouteData.Values​​);
        }        受保护的虚拟无效ProcessAjax(ActionExecutingContext filterContext)
        {
            变种误差= filterContext.Controller.ViewData.ModelState.ToSerializableDictionary();
            VAR JSON =新的JavaScriptSerializer()序列化(错误)。            //发送400状态code(坏请求)
            filterContext.Result =新的HTTPStatus codeResult((INT)的HTTPStatus code.BadRequest,JSON);
        }
    }

修改

这是一个正常(非动作过滤器)PRG模式:

  [HTTPGET]
    公共异步任务&LT;&的ActionResult GT;编辑(GUID ID)
    {
        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果(calendarEvent == NULL)返回this.RedirectToAction&LT; CalendarController&GT;(C =&GT; c.Index());
        VAR模型=新CalendarEditViewModel(calendarEvent);
        ViewData.Model =模型;
        返回查看();
    }    [HttpPost]
    公共异步任务&LT;&的ActionResult GT;编辑(GUID ID,CalendarEventBindingModel绑定)
    {
        如果(!ModelState.IsValid)返回等待编辑(ID);        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果(calendarEvent!= NULL)
        {
            CalendarEvent模型= calendarService.Update(calendarEvent,绑定);
            等待context.SaveChangesAsync();
        }
        返回this.RedirectToAction&所述; CalendarController&GT;(C =&GT; c.Index());
    }

要避免用行动过滤器(或他们的目的)是什么做的是取消对每一个岗位行动ModelState.IsValid检查,所以相同的(用行动过滤器)将是:

  [HTTPGET,ImportModelStateFromTempData]
    公共异步任务&LT;&的ActionResult GT;编辑(GUID ID)
    {
        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果(calendarEvent == NULL)返回this.RedirectToAction&LT; CalendarController&GT;(C =&GT; c.Index());
        VAR模型=新CalendarEditViewModel(calendarEvent);
        ViewData.Model =模型;
        返回查看();
    }    //的ActionResult改为RedirectToRouteResult
    [HttpPost,ValidateModelState]
    公共异步任务&LT; RedirectToRouteResult&GT;编辑(GUID ID,CalendarEventBindingModel绑定)
    {
        //删除ModelState.IsValid检查
        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果(calendarEvent!= NULL)
        {
            CalendarEvent模型= calendarService.Update(calendarEvent,绑定);
            等待context.SaveChangesAsync();
        }
        返回this.RedirectToAction&所述; CalendarController&GT;(C =&GT; c.Index());
    }

这里没有更多的发生。所以,如果你只使用ExportModelState行为过滤器,你将最终获得一个职位的行动是这样的:

  [HttpPost,ExportModelStateToTempData]
    公共异步任务&LT; RedirectToRouteResult&GT;编辑(GUID ID,CalendarEventBindingModel绑定)
    {
        如果返回RedirectToAction(编辑,新的{ID})(ModelState.IsValid!);
        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果(calendarEvent!= NULL)
        {
            CalendarEvent模型= calendarService.Update(calendarEvent,绑定);
            等待context.SaveChangesAsync();
        }
        返回this.RedirectToAction&所述; CalendarController&GT;(C =&GT; c.Index());
    }

这让我问你,为什么你甚至 ActionFilters 在首位麻烦呢?
虽然我不喜欢的ValidateModelState模式,(很多人没有),我真的没有看到任何好处,如果你在你的控制器除了一个场景,那就是你有额外的ModelState错误重定向,完整性让我给你一个例子:

  [HttpPost,ValidateModelState,ExportModelStateToTempData]
    公共异步任务&LT; RedirectToRouteResult&GT;编辑(GUID ID,CalendarEventBindingModel绑定)
    {        VAR calendarEvent =等待calendarService.FindByIdAsync(ID);
        如果((calendarEvent.DateStart&GT;!DateTime.UtcNow.AddDays(7))
            &功放;&安培; binding.DateStart!= calendarEvent.DateStart)
        {
            ModelState.AddModelError(ID,对不起,开始日期不能使用不少于7天的事件更新。);
            返回RedirectToAction(编辑,新的{ID});
        }
        如果(calendarEvent!= NULL)
        {
            CalendarEvent模型= calendarService.Update(calendarEvent,绑定);
            等待context.SaveChangesAsync();
        }
        返回this.RedirectToAction&所述; CalendarController&GT;(C =&GT; c.Index());
    }

在最后一个例子,我同时使用 ValidateModelState ExportModelState ,这是因为 ValidateModelState ActionExecuting 运行,因此它在进入方法的主体,如果绑定有一些验证错误,它会自动重定向之前验证。
然后,我有另一项检查不能在数据注解,因为它加载一个实体​​,看它是否有正确的交易要求(我知道是不是最好的例子,认为它是寻找如果提供的用户名时,可以使用注册我知道有关远程数据注解,但是不包括所有的情况下),那么我只是取决于外部因素别人比的结合我自己的错误,更新的ModelState 。由于 ExportModelState 运行在 ActionExecuted ,我所有的修改的ModelState 持久保存在的TempData 所以我有他们在 HTTPGET 编辑操作。

我知道这一切可以迷惑一些人来说,有没有真正的关于如何做MVC的控制器/ PRG侧好的迹象。我是在做一个博客帖子,以覆盖所有的场景和解决方案苦苦思索。这仅是它的1%。

我希望至少我清除了POST的几个关键点 - 获取工作流程。如果比它有助于这混淆更多信息,请让我知道。很抱歉的长期职位。

我想还需要注意的是有一个的微妙的在PRG返回一个ActionResult的那些返回RedirectToRouteResult差异。
如果你有一个ValidationError,与RedirectToRouteResult后刷新页面(F5),这些错误将不会被持久化,你会得到一个干净的看法,如果你输入的第一次。与那些的ActionResult,你刷新,看到包括错误完全一样的页面。这有什么好做的的ActionResult或RedirectToRouteResult返回类型,它因为在一个场景中,你总是重定向POST,而其他的你成功POST仅重定向。 PRG不建议致盲重定向unsucessfully POST,但有些人preFER做的每一个岗位,这需要TempData的转移重定向。

I implemented the 2nd response to the "Preserve ModelState Errors Across RedirectToAction?" question which involves using two custom ActionFilterAttributes. I like the solution, it keeps the code clean by just adding an attribute to methods that need the functionality.

The solution works well in most cases, but I've run across an issue with a repeating Partial View. Basically I have Partial View that uses it's own Model, separate from the Model that parent view uses.

The simplified version of my code from the main View:

@for (int i = 0; i < Model.Addresses.Count; i++)
{
        address = (Address)Model.Addresses[i];
        @Html.Partial("_AddressModal", address);
}

The Partial View "_AddressModal":

@model Acme.Domain.Models.Address
[...]
@Html.TextBoxFor(model => model.Address1, new { @class = "form-control" } )
[...]

When not using the custom ActionFilterAttributes everything works as expected. With each execution of the Partial View, the lamba expressions such "model => model.Address1" pull the correct value from ModelState.

The problem is when I get redirect and have used the custom ActionFilterAttributes. The core problem is that not only is the ModelState for the one instance of Address updated, but the ModelState of all the Addresses built by the Partial View get overwritten so they contain the same values, instead of the correct instance values.

My question is how do I modify the custom ActionFilterAttributes so that it only updates the ModelState of the one affected Address instance, not all the ModelStates? I want to avoid adding anything to the methods that use the attribute, to keep the clean implementation.

Here is the custom ActionFilterAttributes code from the other question:

public class SetTempDataModelStateAttribute : ActionFilterAttribute
{
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        base.OnActionExecuted(filterContext);         
        filterContext.Controller.TempData["ModelState"] = 
           filterContext.Controller.ViewData.ModelState;
    }
}

public class RestoreModelStateFromTempDataAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        base.OnActionExecuting(filterContext);
        if (filterContext.Controller.TempData.ContainsKey("ModelState"))
        {
            filterContext.Controller.ViewData.ModelState.Merge(
                (ModelStateDictionary)filterContext.Controller.TempData["ModelState"]);
        }
    }
}

解决方案

Check out if this implementation (ben foster) does work : I used it heavily and never had a problem.

Are you setting the attributes correctly ? ' RestoreModelStateFromTempDataAttribute on the get action and SetTempDataModelState on your post action ?

Here are the 4 classes needed (Export, Import, Transfer and Validate)ModelState

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class ExportModelStateToTempDataAttribute : ModelStateTempDataTransfer
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // Only copy when ModelState is invalid and we're performing a Redirect (i.e. PRG)
            if (!filterContext.Controller.ViewData.ModelState.IsValid &&
                (filterContext.Result is RedirectResult || filterContext.Result is RedirectToRouteResult)) 
            {
                ExportModelStateToTempData(filterContext);
            }

            base.OnActionExecuted(filterContext);
        }
    }


 /// <summary>
    /// An Action Filter for importing ModelState from TempData.
    /// You need to decorate your GET actions with this when using the <see cref="ValidateModelStateAttribute"/>.
    /// </summary>
    /// <remarks>
    /// Useful when following the PRG (Post, Redirect, Get) pattern.
    /// </remarks>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class ImportModelStateFromTempDataAttribute : ModelStateTempDataTransfer
    {
        public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            // Only copy from TempData if we are rendering a View/Partial
            if (filterContext.Result is ViewResult)
            {
                ImportModelStateFromTempData(filterContext);
            }
            else 
            {
                // remove it
                RemoveModelStateFromTempData(filterContext);
            }

            base.OnActionExecuted(filterContext);
        }
    }

 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public abstract class ModelStateTempDataTransfer : ActionFilterAttribute
    {
        protected static readonly string Key = typeof(ModelStateTempDataTransfer).FullName;

        /// <summary>
        /// Exports the current ModelState to TempData (available on the next request).
        /// </summary>       
        protected static void ExportModelStateToTempData(ControllerContext context)
        {
            context.Controller.TempData[Key] = context.Controller.ViewData.ModelState;
        }

        /// <summary>
        /// Populates the current ModelState with the values in TempData
        /// </summary>
        protected static void ImportModelStateFromTempData(ControllerContext context)
        {
            var prevModelState = context.Controller.TempData[Key] as ModelStateDictionary;
            context.Controller.ViewData.ModelState.Merge(prevModelState);
        }

        /// <summary>
        /// Removes ModelState from TempData
        /// </summary>
        protected static void RemoveModelStateFromTempData(ControllerContext context)
        {
            context.Controller.TempData[Key] = null;
        }
    }

  /// <summary>
    /// An ActionFilter for automatically validating ModelState before a controller action is executed.
    /// Performs a Redirect if ModelState is invalid. Assumes the <see cref="ImportModelStateFromTempDataAttribute"/> is used on the GET action.
    /// </summary>
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
    public class ValidateModelStateAttribute : ModelStateTempDataTransfer
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            if (!filterContext.Controller.ViewData.ModelState.IsValid)
            {
                if (filterContext.HttpContext.Request.IsAjaxRequest())
                {
                    ProcessAjax(filterContext);
                }
                else
                {
                    ProcessNormal(filterContext);
                }
            }

            base.OnActionExecuting(filterContext);
        }

        protected virtual void ProcessNormal(ActionExecutingContext filterContext)
        {
            // Export ModelState to TempData so it's available on next request
            ExportModelStateToTempData(filterContext);

            // redirect back to GET action
            filterContext.Result = new RedirectToRouteResult(filterContext.RouteData.Values);
        }

        protected virtual void ProcessAjax(ActionExecutingContext filterContext)
        {
            var errors = filterContext.Controller.ViewData.ModelState.ToSerializableDictionary();
            var json = new JavaScriptSerializer().Serialize(errors);

            // send 400 status code (Bad Request)
            filterContext.Result = new HttpStatusCodeResult((int)HttpStatusCode.BadRequest, json);
        }
    }

EDIT

This is a normal (non action filter) PRG pattern :

    [HttpGet]
    public async Task<ActionResult> Edit(Guid id)
    {
        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (calendarEvent == null) return this.RedirectToAction<CalendarController>(c => c.Index());
        var model = new CalendarEditViewModel(calendarEvent);
        ViewData.Model = model;
        return View();
    }

    [HttpPost]
    public async Task<ActionResult> Edit(Guid id, CalendarEventBindingModel binding)
    {
        if (!ModelState.IsValid) return await Edit(id);

        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (calendarEvent != null)
        {
            CalendarEvent model = calendarService.Update(calendarEvent, binding);
            await context.SaveChangesAsync();
        }
        return this.RedirectToAction<CalendarController>(c => c.Index());
    }

What do you want to avoid with action filters (or their purpose) is to remove the ModelState.IsValid check on every post Action, so the same (with action filters) would be :

    [HttpGet, ImportModelStateFromTempData]
    public async Task<ActionResult> Edit(Guid id)
    {
        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (calendarEvent == null) return this.RedirectToAction<CalendarController>(c => c.Index());
        var model = new CalendarEditViewModel(calendarEvent);
        ViewData.Model = model;
        return View();
    }

    // ActionResult changed to RedirectToRouteResult
    [HttpPost, ValidateModelState]
    public async Task<RedirectToRouteResult> Edit(Guid id, CalendarEventBindingModel binding)
    {
        // removed ModelState.IsValid check
        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (calendarEvent != null)
        {
            CalendarEvent model = calendarService.Update(calendarEvent, binding);
            await context.SaveChangesAsync();
        }
        return this.RedirectToAction<CalendarController>(c => c.Index());
    }

there's no much more happening here. So, if you only use ExportModelState action filter, you will end up with a post action like this :

    [HttpPost, ExportModelStateToTempData]
    public async Task<RedirectToRouteResult> Edit(Guid id, CalendarEventBindingModel binding)
    {
        if (!ModelState.IsValid) return RedirectToAction("Edit", new { id });
        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (calendarEvent != null)
        {
            CalendarEvent model = calendarService.Update(calendarEvent, binding);
            await context.SaveChangesAsync();
        }
        return this.RedirectToAction<CalendarController>(c => c.Index());
    }

Which makes me ask you, why you even bother with ActionFilters in the first place ? While I do like the ValidateModelState pattern, (lots of people doesn't), I don't really see any benefit if you are redirecting in your controller except for one scenario, where you have additional modelstate errors, for completeness let me give you an example:

    [HttpPost, ValidateModelState, ExportModelStateToTempData]
    public async Task<RedirectToRouteResult> Edit(Guid id, CalendarEventBindingModel binding)
    {

        var calendarEvent = await calendarService.FindByIdAsync(id);
        if (!(calendarEvent.DateStart > DateTime.UtcNow.AddDays(7))
            && binding.DateStart != calendarEvent.DateStart)
        {
            ModelState.AddModelError("id", "Sorry, Date start cannot be updated with less than 7 days of event.");
            return RedirectToAction("Edit", new { id });
        }
        if (calendarEvent != null)
        {
            CalendarEvent model = calendarService.Update(calendarEvent, binding);
            await context.SaveChangesAsync();
        }
        return this.RedirectToAction<CalendarController>(c => c.Index());
    }

In the last example, I used both ValidateModelState and ExportModelState, this is because ValidateModelState runs on ActionExecuting so it validates before entering the body of the method, if the binding have some validation error it will redirect automatically. Then I have another check that can't be in the data annotations because it deals with loading an entity and seeing if it has the correct requirements (I know is not the best example, think of it as looking if provided username is available when registration, I know about Remote data annotation but doesn't cover all cases) then I just update the ModelState with my own errors depending on external factors others than the binding. Since ExportModelState runs on ActionExecuted, all my modifications to ModelState are persisted on TempData so I will have them on HttpGet Edit action.

I know all this can confuse some of us, there are no really good indications on how to do MVC on the Controller / PRG side. I was thinking hard in making a blog post to cover all the scenarios and solutions. This is only the 1% of it.

I hope at least I cleared few key points of the POST - GET workflow. If this confuses more than it helps, please let me know. Sorry for the long post.

I wanted to note also that there is ONE subtle difference in the PRG returning an ActionResult that the ones returning a RedirectToRouteResult. If you refresh the page (F5) after having a ValidationError, with the RedirectToRouteResult, the errors will NOT be persisted and you get a clean view as if you entered for the first time. With the ActionResult ones, you refresh and see the exact same page including the errors. This has nothing to do with the ActionResult or RedirectToRouteResult return types, its because in one scenario you redirect always on POST while the other you redirect only on success POST. PRG does not suggest to blinding redirect on unsucessfully POST, yet some people prefer to do redirect on every post, which requires TempData transfer.

这篇关于有条件的ModelState合并的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆