ASP.NET MVC 3 - 如何检测当前页面在Post Redirect Get流程中是否被重定向

4
在我的C# .NET 4 MVC 3应用程序中,我有一个删除控制器用于一组CRUD页面,它使用Post Redirect Get模式在成功删除后重定向到Index控制器。如果此页面不是由这样的操作重定向而来,我想在Index页面上呈现一个按钮。是否有一种简单的方法来检测当前页面是否被重定向(即作为PRG重定向的结果到达)?
阅读http://blog.simonlovely.com/archive/2008/11/26/post-redirect-get-pattern-in-mvc.aspx 后,我的当前方法是在DeleteMyEntity方法成功后,在我的删除控制器中使用TempData设置此选项:
try {
    MyService.DeleteMyEntity(MyViewModel.MyEntity);
    TempData["Redirected"] = true;
    args = new RouteValueDictionary(new { Foo = 1, Baa = 2 });
    return RedirectToAction("Index", args);
} catch (Exception e)
{
   //Logging etc. - redirect should never be reached on exception (and TempData item not set)
   throw(e);
}

然后在我的 Index 控制器中,我会检查这个值是否存在且为 true:

if (TempData["Redirected"] != null)
{
    //we can then do something useful with this
}

我看到的另一个机会是向args添加另一项,并在控制器中进行检查,但在这种情况下,我可能只需使用TempData。是否有一种方法可以在请求上使用HTTP响应代码来完成此操作,而无需通过TempData或类似机制传递这些数据?


1
你可以从HttpContext.Current.Request.Headers中读取referer头的值,但这取决于客户端浏览器的良好行为。 TempData基本上是为这种情况设计的,听起来是你最好的选择。 - sargant
Sargant,我应该对环境有相当好的控制。我看了一下,我想不出一个实现方式,在这种方式中我不需要检查引用者是否在我不想为其呈现按钮的引用者列表中。我认为更容易的方法是从我正在重定向的控制器传递标志。 - Aaron Newton
5个回答

5
另一种方法是设置一个全局的ActionFilter,为您“注入”该标志...
public class RedirectDetect: ActionFilterAttribute{
   public override void OnActionExecuted(ActionExecutedContext filterContext){
        if (filterContext.Result is RedirectToRouteResult ||
            filterContext.Result is RedirectResult)
        {
             TempData["Redirected"] = true;
             //or what ever other indicator you want to set
        }
   }
}

然后你可以调用redirectToAction("Index"),然后在接收处理程序中进行检查。

附注:我挑战你大声说出RedirectDetect而不笑。


这是我为了与 cookie 临时数据提供程序一起工作所做的事情,因为在重定向时它会清除 cookie,从而使我的用例无效(PRG 消息)。在提供程序中,我检查标志,然后删除它,同时防止仅有该标志时进行保存。 - kamranicus

3
我类似地使用TempData——例如,在记录已添加/更新/删除后(重定向到)我的视图时显示状态消息。这是TempData用于的简单而丢弃的东西,所以我认为你所拥有的是适当的。
就个人而言,除非我绝对需要它,否则我不会去搞HTTP状态码。而且你可能可以利用referrer http头来做些事情,但再次,那将比仅使用TempData更加混乱和复杂。你拥有一个干净、简单的解决方案,它能够正常工作,我认为你应该沿用现有方案。

谢谢hawkke。我唯一的担忧是其他程序员/未来参与该项目的自己必须意识到这个约定,而它并没有像ViewModel那样被绑定,后者会为此提供一个相当具体的使用/智能提示。但是,由于我对其有很多控制权,这可能是最好的解决方案。 - Aaron Newton
我避免使用TempData,因为它要求用户假定客户端浏览器总是会发出后续请求。这违反了“永远不要相信客户端”的规则。 - Dai
@AaronNewton 是的,你必须意识到这个约定;但是,你必须意识到很多约定,因为MVC高度基于模式和约定(例如,你正在使用的整个PRG模式)。据我所知,这是一种相当常见的实现方式,所以这只是一个要保留在工具箱中的约定 :) - hawkke
@AaronNewton,你可以将功能封装在一个过滤器属性中,使其更加自我记录。 - quentin-starin
1
谢谢qes。我刚刚发现您可以通过这样的方法覆盖基类中的控制器:protected override void OnActionExecuted(ActionExecutedContext filterContext)。我可以在这个基类中为控制器设置属性(例如bool Redirected),并且如果继承类中的控制器中没有设置该值(例如在方法Delete或整个控制器类中设置),则会在 OnActionExecuted 中抛出异常。然后使用此值修改ViewModel中的属性,假设ViewModel也继承了一个基类。 - Aaron Newton
1
请参阅http://forums.asp.net/post/2444434.aspx和http://msdn.microsoft.com/en-us/library/system.web.mvc.controller.onactionexecuted(v=vs.98).aspx。如果我成功了,我会尝试发布一小段代码片段。 - Aaron Newton

2

我不知道有任何更简单的机制,而且已经使用TempData相当长一段时间来实现Post-Redirect-Get功能。据我所知,这正是TempData存在的原因之一。我将继续使用它。


谢谢 qes。另一个考虑因素是,如果它有效,请使用它:D - Aaron Newton

1

无法区分由于3xx重定向还是用户发起的GET请求。最好的方法是提供一个查询字符串参数,该参数仅在初始POST请求的重定向中附加,但是没有阻止用户使用相同的查询字符串重新加载页面。

或者,您可以通过从POST重定向发送cookie,然后在随后的GET响应中删除cookie,如下所示:

public ActionResult PostHandler(ViewModel model) {
    SetCookie("redirected", "true"); // psuedocode
    return Redirect("GetHandler2");
}

public ActionResult GetHandler2() {
    if( GetCookie("redirected") == "true" ) {
        // do something
    }
    DeleteCookie("redirected");
}

戴,我会尝试一下。 - Aaron Newton

0

在George的回答基础上构建:

public class MarkRedirects : ActionFilterAttribute
{
   public override void OnActionExecuted(ActionExecutedContext context)
   {
        if (context.Result is RedirectToActionResult ||
            context.Result is RedirectResult)
        {
            // Obtain and verify the underlying IController
            var controller = context.Controller as Controller;
            if (controller != null)
                controller.TempData["Redirected"] = true; // or set other dictionary data here
        }
   }
}

根据您重定向的方式不同,context.Result的条件检查可能会有所变化,例如如果您通过RedirectToAction()方法重定向用户,则context.Result is RedirectToActionResult将返回true,但context.Result is RedirectToRouteResult则不会。

因此,您需要根据个人喜好更改该条件,以确定如何重定向用户。当前的代码适用于OP的情况。

如果您要在多个地方使用此代码,则修改基类控制器可能是明智的选择。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接