ASP.net MVC - 使用模型绑定器,无需查询字符串值或花哨的路由

发布于 2024-10-14 17:46:48 字数 1615 浏览 6 评论 0原文

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

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

不同的 URL 使用内置模型绑定器自动映射到 Action Method 参数。我想继续使用标准模型绑定器,但我需要删除查询字符串。我们希望将这些网址放在 CDN 不支持因查询字符串而异的资源(Amazon Cloud front),因此我们需要从 URL 中删除问号并做一些像这样的愚蠢的事情

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
    );

选项 1(首选):OnActionExecuting< /强> 我计划在控制器操作使用控制器中的 OnActionExecuting 方法执行之前,使用上面路由中的包罗万象的“查询”值将旧查询字符串注入到默认模型绑定器中。但是,我有点不确定是否可以添加回问号。 我可以这样做吗?您建议如何修改 URL?

选项 2:自定义模型绑定器 我还可以制作某种自定义模型绑定器,它只是告诉默认模型绑定器将“查询”值视为查询字符串。 您更喜欢这种方法吗?你能给我举一个相关的例子吗?

我有点担心这是一个边缘情况,并且在我开始尝试实现选项 1 或选项 2 并偶然发现不可预见的错误之前希望得到一些意见。

I have an Asp.net MVC app that currently works well using the default model binder and urls with complex parameters like this:

example.com/Controller/Action?a=hello&b=world&c=1&d=2&e=3 (notice the question mark)

The different urls automatically map to Action Method parameters using the built in model binder. I would like to continue using the standard model binder but I need to get rid of the query string. We want to put these urls behind a CDN that does not support resources that vary by query strings (Amazon Cloud front) so we need to remove the question mark from our urls and do something silly like this

example.com/Controller/Action/a=hello&b=world&c=1&d=2&e=3 (no question mark)

These urls are only used via AJAX, so I'm not interested in making them user or SEO friendly. I want to just drop the question mark and keep all my code exactly the same. The hitch is, I'm unsure about how to keep using the MVC model binder and abandoning it would be a lot of work.

I don't want to use a complex route to map my objects like this question did and, instead, I am planning to use a single simple route like the one below

   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
    );

Option 1 (preferred): OnActionExecuting
I plan to use the catchall "query" value in the route above to inject the old query string into the default model binder before the Controller Actions execute using the OnActionExecuting method in my controller. However, I'm a bit unsure if I can just add back the question mark. Can I do this? How would you recommend modifying the url?

Option 2: Custom Model Binder
I also could make some sort of Custom Model Binder that just tells the default model binder to treat the "query" value like a query string. Would you prefer this method? Can you point me to a relevant example?

I am a bit worried that this is an edge case and would love some input before I start trying to implement Option 1 or Option 2 and stumble onto unforseen bugs.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

羞稚 2024-10-21 17:46:48

您可以使用带有包罗万象的路由的自定义值提供程序:

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

值提供程序

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
        );
    }
}

以及您在 Application_Start 中注册的

ValueProviderFactories.Factories.Add(new MyCustomProvider());

: ,现在剩下的就是模型:

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; }
}

和控制器:

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

,然后导航到: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=""/>

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

You could use a custom value provider with a catchall route:

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

and the value provider:

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
        );
    }
}

which you register in Application_Start:

ValueProviderFactories.Factories.Add(new MyCustomProvider());

and now all that's left is a model:

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; }
}

and a controller:

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

and then navigate to: NoQueryString/Home/Index/a=hello&b=world&c=1&d=2&e=3. The Index is hit and the model is bound.

Remark: Notice the ValidateInput(false) on the controller action. That's probably gonna be needed because ASP.NET won't allow you to use special characters such as & as part of a URI. You might also need to tweak your web.config a little:

<httpRuntime requestValidationMode="2.0" requestPathInvalidCharacters=""/>

For more information about those tweaks make sure you have read Scott Hansleman's blog post.

唠甜嗑 2024-10-21 17:46:48

“复杂”路由定义有什么问题?您链接到的问题的路线非常非常简单。默认值可以有很长的路要走。更好地使用 MVC 的默认功能来转变:

example.com/Controller/Action/a=hello&b=world&c=1&d=2&e=3

example.com/Controller/Action/hello/world/1/2/3

开始重新连接核心部分,如 RouteValue 处理(选项 1)或实现自定义模型绑定器。

我知道这并不能回答您的问题,但您似乎在不了解技术影响的情况下选择了不使用 MVC 的路由功能。你知道他们怎么说“好朋友会阻止对方做蠢事”。

Whats the problem with "complex" routing definitions? The routes the question you linked to are very very simple. Defaults can go a long way. Better to use the default capabilities of MVC to turn:

example.com/Controller/Action/a=hello&b=world&c=1&d=2&e=3

into

example.com/Controller/Action/hello/world/1/2/3

than start re-wiring core bits like RouteValue handling ( option 1 ) or implementing a custom model binder.

I know this doesn't answer your question but it seems like you made a choice about not using MVC's routing capabilities without understanding the technical ramifications. You know what they say "good friends stop each other from doing stupid things".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文