【问题标题】:How can I modify an OData query to add access filters?如何修改 OData 查询以添加访问过滤器?
【发布时间】:2016-06-08 22:21:18
【问题描述】:

我有一个 C# OData 端点,我需要评估与 OData 查询一起提交的过滤器,以确定是否需要添加其他过滤器来限制返回给用户的结果。

我的示例模型很简单:

学生 -> 图书目录 -> 图书 (所有实体都有一个 CampusId 属性分配给它们。)

当属于 Campus #5 的用户执行以下查询时: "学生 $select=Id,Name,CampusId" 它应该转换为: "Students$select=Id,Name,CampusId&$filter=CampusId eq 5"

我可以通过简单的查询来做到这一点,只需将过滤器添加为字符串。

我真正想做的是: 1) 确定选择和扩展的实体 2) 确定这些实体中的任何一个是否具有 CampusId 属性 3) 将必要的过滤器值添加到 Uri 中,以便将每个选定和/或扩展实体的查询过滤到该 Campus

我正在尝试使用 Microsoft.OData.Core.UriParser.ODataUriParser 来解析过滤器值,然后创建一个新的 Uri。

例如:

var parser = new Microsoft.OData.Core.UriParser.ODataUriParser(edmModel, new Uri(serviceRootPath), originalUri);
var filter = parser.ParseFilter();

使用上面的代码 sn-p,您可以获得“过滤器”变量,以提供一种 Microsoft.OData.Core.UriParser.Semantic.FilterClause 类型,可用于检查当前过滤器值在 OData 查询 Uri 中。

有谁知道如何编辑 FilterClause 中的值以便能够向 Uri 添加新的过滤器值?

我没有找到很多关于如何编辑 FilterClause 以生成具有新过滤器值的更新 Uri 的示例。

【问题讨论】:

  • 我在这里有一个答案来更改控制器中的过滤器值:stackoverflow.com/questions/37339114/…
  • 谢谢,但这无济于事。现实情况是,您提到的该响应中的答案仅在您已经知道 OData Uri 字符串的结构时才有效。实际上,过滤器值可以包括各种其他设置(startsWith、Contains 等),因此简单的字符串替换不会可靠地工作。正确的解决方案应该允许我们检查现有的过滤器表达式树并附加到它(如果需要),而不会干扰它已经包含的任何现有过滤器值。
  • 对于它的价值,我想我在这里问了一个几乎相同的问题:stackoverflow.com/questions/33126251/… 还没有一个很好的答案,但它可能会给你一些关于这个问题的更好的背景信息.

标签: filter nested odata


【解决方案1】:

我通过编写一个算法找到了我的问题的解决方案,该算法根据我的模型的属性向请求 ODataUri 添加了额外的过滤。它检查根级别实体的任何属性以及任何扩展实体的属性,以确定要添加到 OData 查询的其他过滤器表达式。

OData v4 支持在 $expand 子句中进行过滤,但扩展实体中的 filterOption 是只读的,因此您无法修改扩展实体的过滤器表达式。您只能检查展开实体处的 filterOption 内容。

我的解决方案是检查所有实体(根实体和扩展实体)的属性,然后在请求 ODataUri 的根过滤器中添加我需要的任何其他 $filter 选项。

这是一个 OData 请求 URL 示例:

/RootEntity?$expand=OtherEntity($expand=SomeOtherEntity)

这是我更新后的同一个 OData 请求 URL:

/RootEntity?$filter=OtherEntity/SomeOtherEntity/Id eq 3&$expand=OtherEntity($expand=SomeOtherEntity)

我用来完成此操作的步骤:

  1. 使用 ODataUriParser 将传入的 Url 解析为 Uri 对象

见下文:

var parser = new ODataUriParser(model, new Uri(serviceRootPath), requestUri);   
var odataUri = parser.ParseUri();
  1. 创建一个方法,该方法将从根向下遍历到所有展开的实体,并通过 ref 传递 ODataUri(以便您可以在检查每个实体时根据需要对其进行更新)

第一种方法将检查根实体并根据根实体的属性添加任何其他过滤器。

AddCustomFilters(ref ODataUri odataUri);

AddCustomFilters 方法将遍历展开的实体并调用 AddCustomFiltersToExpandedEntity,该方法将继续向下遍历所有展开的实体以添加任何必要的过滤器。

foreach (var item in odatauri.SelectAndExpand.SelectedItems)
{
    AddCustomFiltersToExpandedEntity(ref ODataUri odataUri, ExpandedNavigationSelectItem expandedNavigationSelectItem, string parentNavigationNameProperty)
}

AddCustomFiltersToExpandedEntity 方法应该调用自身,因为它会循环每个级别的展开实体。

  1. 在检查每个实体时更新根过滤器

使用您的其他过滤器要求创建一个新的过滤器子句,并覆盖根级别的现有过滤器子句。 ODataUri 根级别的 $filter 有一个设置器,因此可以覆盖它。

odataUri.Filter = new FilterClause(newFilterExpression, newFilterRange);

注意:我使用 BinaryOperatorKind.And 创建了一个新的过滤器子句,以便将任何其他过滤器表达式简单地附加到 ODataUri 中已有的任何现有过滤器表达式

var combinedFilterExpression = new BinaryOperatorNode(BinaryOperatorKind.And, odataUri.Filter.Expression, newFilterExpression);
odataUri.Filter = new FilterClause(combinedFilterExpression, newFilterRange);
  1. 使用 ODataUriBuilder 根据更新后的 Uri 创建新的 Url

见下文:

var updatedODataUri = new Microsoft.OData.Core.UriBuilder.ODataUriBuilder(ODataUrlConventions.Default, odataUri).BuildUri();
  1. 将请求 Uri 替换为更新后的 Uri。

这允许 OData 控制器使用更新的 OData Url 完成对请求的处理,其中包括您刚刚添加到根级别文件管理器的其他过滤器选项。

ActionContext.Request.RequestUri = updatedODataUri;

这使我能够添加我需要的任何过滤选项,并 100% 确保我没有错误地更改 OData Url 结构。

我希望这对其他人遇到同样的问题时有所帮助。

【讨论】:

  • 它接缝在 AspNetCore 2 中,序列化添加的额外过滤条件不会添加父实体:我创建了一个问题 github.com/OData/WebApi/issues/1240 。你现在在 AspNetCore 上运行吗?
猜你喜欢
  • 1970-01-01
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-17
  • 1970-01-01
相关资源
最近更新 更多