MVC路由基于POST参数

时间:2011-02-17 16:09:42

标签: model-view-controller asp.net-mvc-routing

我们有一个PHP应用程序,我们正在转换为MVC。目标是使应用程序在URL和HTML方面保持相同(SEO等等+ PHP站点仍在处理中)。我们有一个由3个视图组成的预订流程,在当前的PHP站点中,所有这些视图都回发到相同的URL,发送隐藏字段以区分预订过程中的哪个页面/步骤被发回(页面之间的数据存储在查询建立的状态下)。

要在MVC中复制它,我们可以使用一个单独的操作方法,所有3个页面都会发布,只有一个绑定器只根据发布的页面填充模型的一部分,并且控制器会查看模型并决定预订过程中的下一个阶段。或者如果这可能(这是我的问题),设置一个可以读取POST参数的路由,并根据POST参数的值,路由到不同的操作方法。

据我所知,MVC路由中没有对此的支持,因为它代表(但我希望在这方面是错误的),那么我需要在哪里考虑扩展MVC才能支持这一点? (我认为多种动作方法更加清晰。)

非常感谢您的帮助。

1 个答案:

答案 0 :(得分:2)

我遇到了两个解决方案,一个由我工作的人设计,另一个是我更优雅的解决方案!

第一个解决方案是指定一个扩展MVcRouteHandler指定路由的类。此路由处理程序可以检查HttpContext的Form中的路径,读取Form数据,然后更新RequestContext中的RouteData

MapRoute(routes, 
            "Book",
            "{locale}/book",
            new { controller = "Reservation", action = "Index" }).RouteHandler = new ReservationRouteHandler();

ReservationRouteHandler如下所示:

public class ReservationRouteHandler: MvcRouteHandler
{
    protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
    {
        var request = requestContext.HttpContext.Request;

        // First attempt to match one of the posted tab types
        var action = ReservationNavigationHandler.GetActionFromPostData(request);

        requestContext.RouteData.Values["action"] = action.ActionName;
        requestContext.RouteData.Values["viewStage"] = action.ViewStage;

        return base.GetHttpHandler(requestContext);
    }        

NavigationHandler实际上负责查看表单数据,但您明白了。

这个解决方案有效,然而,感觉有点笨拙,从查看控制器类,你永远不会知道这发生了,也不会意识到为什么en-gb / book会指向不同的方法,更不用说这个了并不真的觉得可重复使用。

更好的解决方案是在控制器上重载方法,即在这种情况下它们都被称为book,然后定义自己的客户ActionMethodSelectorAttribute。这就是HttpPost属性的源自。

 public class FormPostFilterAttribute : ActionMethodSelectorAttribute
{
    private readonly string _elementId;
    private readonly string _requiredValue;

    public FormPostFilterAttribute(string elementId, string requiredValue)
   { 
        _elementId = elementId;
        _requiredValue = requiredValue;
    }

    public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
    {
        if (string.IsNullOrEmpty(controllerContext.HttpContext.Request.Form[_elementId]))
        {
            return false;
        }

        if (controllerContext.HttpContext.Request.Form[_elementId] != _requiredValue)
        {
            return false;
        }

        return true;
    }
}

MVC在尝试在给定URL的控制器上解析正确的操作方法时调用此类。然后我们将动作方法声明如下:

public ActionResult Book(HotelSummaryPostData hotelSummary)
    {
        return View("CustomerDetails");
    }

    [FormFieldFilter("stepID", "1")]
    public ActionResult Book(YourDetailsPostData yourDetails, RequestedViewPostData requestedView)
    {
        return View(requestedView.RequestedView);
    }

    [FormFieldFilter("stepID", "2")]
    public ActionResult Book(RoomDetailsPostData roomDetails, RequestedViewPostData requestedView)
    {
        return View(requestedView.RequestedView);
    }

    [HttpGet]
    public ActionResult Book()
    {
        return View();
    }

我们必须在不同的页面上定义隐藏字段stepID,以便当这些页面上的表单回发到公共URL时,SelectorAttributes正确地确定要调用的操作方法。当一个名字相同的方法存在而没有属性集时,我很惊讶它正确地选择了一个动作方法,但也很高兴。

我没有考虑过你是否可以堆叠这些方法选择器,我想你可以在MVC中使用这个非常酷的功能。

我希望这个答案对我以外的人有用。 :)