我遇到了两种解决方案,一种是我的同事设计的,另一种是我设计的更优雅的解决方案!
第一个解决方案是为指定的路由指定一个扩展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 中的这个功能非常酷。
我希望这个答案对我以外的其他人有用。 :)