ASP.net MVC - 使用Model Binder而不使用查询字符串值或花哨的路由

Gle*_*enn 2 data-binding asp.net-mvc model model-binding modelbinders

我有一个Asp.net MVC应用程序,目前使用默认模型绑定器和具有复杂参数的URL,如下所示:

example.com/Controller/Action?a=hello&b=world&c=1&d=2&e=3 (注意问号)

不同的URL使用内置的模型绑定器自动映射到Action Method参数.我想继续使用标准模型绑定器,但我需要摆脱查询字符串.我们希望将这些网址放在CDN之后,该CDN不支持因查询字符串(Amazon Cloud front)而异的资源,因此我们需要从我们的网址中删除问号并执行类似这样的操作

example.com/Controller/Action/a=hello&b=world&c=1&d=2&e=3 (无问号)

这些网址只能通过AJAX使用,所以我对使用户或SEO友好不感兴趣.我想删除问号并保持我的所有代码完全相同.问题是,我不确定如何继续使用MVC模型绑定器并放弃它将是一项很多工作.

我不想使用复杂的路线来映射我的对象,就像这个问题一样,相反,我打算使用一条简单的路线,如下所示

   routes.MapRoute(
        "NoQueryString",                    // Route name
        "NoQueryString/{action}/{query}", // 'query' = querystring without the ?
        new {
            controller = "NoQueryString",
            action = "Index",
            query = "" }  // want to parse with model binder - By NOT ROUTE
    );
Run Code Online (Sandbox Code Playgroud)

选项1(首选):OnActionExecuting 我计划在控制器动作使用我的控制器中的OnActionExecuting方法执行之前,使用上面路由中的catchall"query"值将旧的查询字符串注入默认模型绑定器.但是,我有点不确定我是否可以添加问号. 我可以这样做吗?你会如何建议修改网址?

选项2:自定义模型Binder 我也可以创建某种自定义模型绑定器,它只是告诉默认模型绑定器将"查询"值视为查询字符串. 你更喜欢这种方法吗?你能指点我一个相关的例子吗?

我有点担心这是一个边缘情况,并且在我开始尝试实现选项1或选项2并偶然发现不可预见的错误之前会喜欢一些输入.

Dar*_*rov 5

您可以将自定义值提供程序与catchall路由一起使用:

routes.MapRoute(
    "NoQueryString",
    "NoQueryString/{controller}/{action}/{*catch-em-all}",
    new { controller = "Home", action = "Index" }
);
Run Code Online (Sandbox Code Playgroud)

和价值提供者:

public class MyCustomProvider : ValueProviderFactory
{
    public override IValueProvider GetValueProvider(ControllerContext controllerContext)
    {
        var value = controllerContext.RouteData.Values["catch-em-all"] as string;
        var backingStore = new Dictionary<string, object>();
        if (!string.IsNullOrEmpty(value))
        {
            var nvc = HttpUtility.ParseQueryString(value);
            foreach (string key in nvc)
            {
                backingStore.Add(key, nvc[key]);
            }
        }
        return new DictionaryValueProvider<object>(
            backingStore, 
            CultureInfo.CurrentCulture
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

你注册的是Application_Start:

ValueProviderFactories.Factories.Add(new MyCustomProvider());
Run Code Online (Sandbox Code Playgroud)

现在剩下的只是一个模型:

public class MyViewModel
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public string D { get; set; }
    public string E { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

和一个控制器:

public class HomeController : Controller
{
    [ValidateInput(false)]
    public ActionResult Index(MyViewModel model)
    {
        return View(model);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后导航到:NoQueryString/Home/Index/a=hello&b=world&c=1&d=2&e=3.在Index被击中和模型绑定.

备注:注意ValidateInput(false)控制器上的操作.这可能是需要的,因为ASP.NET不允许您使用特殊字符,例如&URI的一部分.您可能还需要稍微调整一下web.config:

<httpRuntime requestValidationMode="2.0" requestPathInvalidCharacters=""/>
Run Code Online (Sandbox Code Playgroud)

有关这些调整的更多信息,请确保您已阅读Scott Hansleman的博客文章.