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

我希望這個答案對我以外的人有用。 :)