【问题标题】:MVC Routes based on POST parameters基于 POST 参数的 MVC 路由
【发布时间】:2011-02-17 16:09:42
【问题描述】:

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

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

据我所知,在 MVC 路由中不支持这一点(但我很想在这方面出错),那么我需要在哪里扩展 MVC 以支持这一点? (我认为多种操作方法更清洁)。

非常感谢您的帮助。

【问题讨论】:

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


    【解决方案1】:

    我遇到了两种解决方案,一种是我的同事设计的,另一种是我设计的更优雅的解决方案!

    第一个解决方案是为指定的路由指定一个扩展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 中的这个功能非常酷。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-01
      • 1970-01-01
      相关资源
      最近更新 更多