ASP.NET MVC - 清理复杂模型绑定场景中的 URL

发布于 2024-09-10 08:42:56 字数 1841 浏览 5 评论 0原文

在我正在构建的一个应用程序中,我构建了一个非常灵活的基于属性的系统,用于描述数据库中的产品,其中每个产品可以分配不确定数量的属性,每个属性都有一个“类型”。因此,例如,一种属性类型可能是“类别”,分配给单个属性的值可能类似于“卡车”。分配给给定产品的属性数量没有限制,并且由于属性和属性类型与产品一起存储在数据库中,因此我的应用程序提前不知道它们是什么。

给定属性类型的选项的特征之一是它是否是“可搜索的”。如果属性可搜索,我可以使用其值与其类型名称配对来搜索/过滤我的产品。因此,例如,用户可能想要返回属性类型“类别”等于“卡车”且属性类型“颜色”等于“红色”的所有产品。那里没有什么太独特的。

我遇到的麻烦是,因为我的系统事先不知道我的属性类型名称是什么,所以我无法轻松创建一个接受可读格式参数的操作方法,例如 stringcategory字符串颜色。作为解决方案,我利用了 DefaultModelBinder 对绑定到字典的支持。通过这种方法,我只需要以正确的结构格式化我的字段名称,然后我的操作方法就可以接受 IDictionary。参数。这一切都工作得相当好,但当用户通过单个参数(即“查看卡车类别中的更多产品”)执行基于链接的过滤器时,它会产生一些非常令人讨厌的 URL。使用 DefaultModelBinder 绑定到字典时,要求您的字段命名模式类似于以下内容:

<input type="hidden" name="parameters[0].Key" value="Category" />
    <select name="parameters[0].Value">
    <option value="Trucks">Trucks</option>
    <option value="Compacts">Compacts</option>
    <option value="SUVs">SUVs</option>
</select>
<input type="hidden" name="parameters[1].Key" value="Manufacturer" />
<select name="parameters[1].Value">
    <option value="Ford">Ford</option>
    <option value="Toyota">Toyota</option>
    <option value="Honda">Honda</option>
</select>

这不仅非常冗长,而且还有些令人沮丧,因为每个键/值必须在字段名称中包含序数索引。虽然这对于 POST 表单来说是可以接受的,但在 GET URL 中并不是特别理想,因为我们最终得到的 URL 类似于 ?parameters[0].Key=Category&parameters[0].Value=Trucks&parameters[1 ].Key=制造商&参数[1].Value=福特。这不仅丑陋,而且其实现也非常有限,因为对 URL 的任何修改都可能会破坏整个结果集(如果用户只想通过修改 URL 来按第二个参数进行搜索,则必须删除第一个参数并对整个集合进行适当的重新编号)。

我正在寻找一种更好的方法来处理这种情况。理想情况下,我只想有一个查询字符串值 ?Category=Red 并进行相应的过滤,但是我的操作方法不知道是否确实有要绑定的“类别”参数。两者之间是否有任何可以让我拥有更干净的查询字符串参数,而不会造成如此糟糕的 URL 结构?

我正在考虑是否可以构建自己的自定义 ModelBinder,但如果有其他方法,我想避免这种情况。

In one of the applications I am building I have constructed a very flexible attribute-based system for describing products in my database wherein each product can have an indeterminate number of attributes assigned to it with each attribute having a single "type". So, for example, one attribute type might be "Category" and the value assigned to a single attribute would be something like "Trucks". There are no restrictions on the number of attributes assigned to a given product and because the attributes and attribute types are stored in the database alongside the products, my application does not know ahead of time what any of them will be.

One of the features of the options for a given attribute type is whether or not it is "searchable". In the event of an attribute being searchable I can then use its value paired with its type name to search/filter my products. So, for example, a user might want to return all products having the attribute type "Category" equal "Trucks" and attribute type "Color" equal "Red". Nothing too unique there.

The trouble I am dealing with is that because my system does not know ahead of time what my attribute type names are, I cannot easily create an action method accepting parameters in a readable format like string category or string color. As a solution I have made use of the DefaultModelBinder's support for binding to a dictionary. With this approach I need only format my field names in the correct structure, and then my action method can accept an IDictionary<string,string> parameters. This all works fairly well, but it makes for some really nasty URLs when the user is performing a link-based filter by a single parameter, i.e. "See more Products in Category Trucks". With the DefaultModelBinder binding to a dictionary requires that your field naming pattern resembles the following:

<input type="hidden" name="parameters[0].Key" value="Category" />
    <select name="parameters[0].Value">
    <option value="Trucks">Trucks</option>
    <option value="Compacts">Compacts</option>
    <option value="SUVs">SUVs</option>
</select>
<input type="hidden" name="parameters[1].Key" value="Manufacturer" />
<select name="parameters[1].Value">
    <option value="Ford">Ford</option>
    <option value="Toyota">Toyota</option>
    <option value="Honda">Honda</option>
</select>

Not only is this incredibly verbose, but it also somewhat frustrating due to the fact that each Key/Value must contain an ordinal index in the field name. Although this is acceptable for a POST form, it is not particularly ideal in a GET URL because we end up with URLS resembling ?parameters[0].Key=Category¶meters[0].Value=Trucks¶meters[1].Key=Manufacturer¶meters[1].Value=Ford. Not only is this ugly, it is very limited in its implementation because any modification to the URL could potential destroy the entire result set (if the user wanted to just search by the second parameter via modifying the URL they would have to remove the first parameter and renumber the whole collection appropriately).

What I am looking for is a better way to handle this kind of situation. Ideally I'd like to simply have a querystring value ?Category=Red and filter accordingly, but then my action method doesn't know if there actually is a "Category" parameter to bind to. Is there any in between that would allow me to have cleaner querystring parameters that wouldn't make for such awful URL structures?

I was thinking about possibly building my own custom ModelBinder, but I'd like to avoid that if there's another way.

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

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

发布评论

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

评论(1

与君绝 2024-09-17 08:42:56

我更喜欢你的“干净”URI:?Category=Red。那么让我们从这里开始,看看它是如何工作的。

您可以在运行时加载所有类别,对吧?我突然想到:

IEnumerable<string> allCategories = Categories.GetAll();
var usedCategories = Request.QueryString.AllKeys.Intersect(allCategories);
var search = from c in usedCategories
             select new 
             {
                 Key = c,
                 Value = Request.QueryString[c]
             };

您可以按原样使用它,或者制作一个自定义模型活页夹。无论哪种情况,代码都不是很多。

I much prefer your "clean" URIs: ?Category=Red. So let's start there and see how it could work.

You can load up all the categories at runtime, right? Off the top of my head:

IEnumerable<string> allCategories = Categories.GetAll();
var usedCategories = Request.QueryString.AllKeys.Intersect(allCategories);
var search = from c in usedCategories
             select new 
             {
                 Key = c,
                 Value = Request.QueryString[c]
             };

You could use this as-is, or make a custom model binder. In either case, it's not a lot of code.

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