2011-02-17 113 views
0

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

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

据我了解,在MVC路由中不存在对此的支持(但我很想在此做出错误),所以我需要考虑扩展MVC以支持这一点? (我认为多种行动方法在某种程度上更清洁)。

您的帮助将不胜感激。

回答

1

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

第一种解决方案是为指定的路线指定延伸MVcRouteHandler的类。此路由处理程序可以检查HttpContext表单中的路由,读取表单数据,然后在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,然后定义自己的custome 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; 
    } 
} 

当它尝试解析给定URL的控制器上的正确操作方法时,MVC会调用此类。然后,我们申报操作方法如下:

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中非常酷的功能。

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