OData V4 在服务器端修改 $filter
Posted
技术标签:
【中文标题】OData V4 在服务器端修改 $filter【英文标题】:OData V4 modify $filter on server side 【发布时间】:2016-02-13 03:21:53 【问题描述】:我希望能够修改控制器内部的过滤器,然后根据更改后的过滤器返回数据。
所以我在服务器端有一个 ODataQueryOptions 参数,我可以用它来查看 FilterQueryOption。
假设过滤器类似于“$filter=ID eq -1”,但在服务器端,如果我看到 ID 为“-1”,这告诉我用户想要选择所有记录。
我尝试将“$filter=ID eq -1”更改为“$filter=ID ne -1”,这将通过设置 Filter.RawValue 为我提供所有信息,但这是只读的。 我尝试创建一个新的 FilterQueryOption,但这需要一个 ODataQueryContext 和一个 ODataQueryOptionParser,我不知道如何创建。
然后我尝试设置 Filter = Null,然后设置 ApplyTo,当我在控制器中设置断点并在即时窗口上检查它时,它似乎可以工作,但是一旦它离开控制器上的 GET 方法,然后它“恢复”返回到 URL 中传递的内容。
这篇文章讨论了做一些非常类似“The best way to modify a WebAPI OData QueryOptions.Filter”的事情,但是一旦离开控制器的 GET 方法,它就会恢复到 URL 查询过滤器。
使用示例代码更新
[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
if (queryOptions.Filter != null)
var url = queryOptions.Request.RequestUri.AbsoluteUri;
string filter = queryOptions.Filter.RawValue;
url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
return query as IQueryable<Product>;
运行此代码不会返回任何产品,这是因为 URL 中的原始查询想要产品 1,而我将产品 1 的 ID 过滤器换成了产品 2。 现在,如果我运行 SQL Profiler,我可以看到它添加了类似“Select * from Product WHERE ID = 1 AND ID = 2”的内容。
但是如果我通过替换 $top 来尝试同样的事情,那么它可以正常工作。
[EnableQuery]
[HttpGet]
public IQueryable<Product> GetProducts(ODataQueryOptions<Product> queryOptions)
if (queryOptions.Top != null)
var url = queryOptions.Request.RequestUri.AbsoluteUri;
string filter = queryOptions.Top.RawValue;
url = url.Replace("$top=2", "$top=1");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions<Product>(queryOptions.Context, req);
IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
return query as IQueryable<Product>;
结束结果 在微软的帮助下。这是支持过滤、计数和分页的最终输出。
using System.Net.Http;
using System.Web.OData;
using System.Web.OData.Extensions;
using System.Web.OData.Query;
/// <summary>
/// Used to create custom filters, selects, groupings, ordering, etc...
/// </summary>
public class CustomEnableQueryAttribute : EnableQueryAttribute
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
IQueryable result = default(IQueryable);
// get the original request before the alterations
HttpRequestMessage originalRequest = queryOptions.Request;
// get the original URL before the alterations
string url = originalRequest.RequestUri.AbsoluteUri;
// rebuild the URL if it contains a specific filter for "ID = 0" to select all records
if (queryOptions.Filter != null && url.Contains("$filter=ID%20eq%200"))
// apply the new filter
url = url.Replace("$filter=ID%20eq%200", "$filter=ID%20ne%200");
// build a new request for the filter
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
// reset the query options with the new request
queryOptions = new ODataQueryOptions(queryOptions.Context, req);
// set a top filter if one was not supplied
if (queryOptions.Top == null)
// apply the query options with the new top filter
result = queryOptions.ApplyTo(queryable, new ODataQuerySettings PageSize = 100 );
else
// apply any pending information that was not previously applied
result = queryOptions.ApplyTo(queryable);
// add the NextLink if one exists
if (queryOptions.Request.ODataProperties().NextLink != null)
originalRequest.ODataProperties().NextLink = queryOptions.Request.ODataProperties().NextLink;
// add the TotalCount if one exists
if (queryOptions.Request.ODataProperties().TotalCount != null)
originalRequest.ODataProperties().TotalCount = queryOptions.Request.ODataProperties().TotalCount;
// return all results
return result;
【问题讨论】:
【参考方案1】:删除 [EnableQuery] 属性,您的场景应该可以工作,因为使用此属性后,OData/WebApi 将在您在控制器中返回数据后应用您的原始查询选项,如果您已经在控制器方法中应用,那么您不应该使用该属性。
但是如果你的查询选项包含$select,那么这些代码就不起作用了,因为结果的类型不是Product,我们使用一个包装器来表示$select的结果,所以我建议你试试这个:
制作自定义的 EnableQueryAttribute
public class MyEnableQueryAttribute : EnableQueryAttribute
public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
if (queryOptions.Filter != null)
queryOptions.ApplyTo(queryable);
var url = queryOptions.Request.RequestUri.AbsoluteUri;
url = url.Replace("$filter=Id%20eq%201", "$filter=Id%20eq%202");
var req = new HttpRequestMessage(HttpMethod.Get, url);
queryOptions = new ODataQueryOptions(queryOptions.Context, req);
return queryOptions.ApplyTo(queryable);
在你的控制器方法中使用这个属性
[MyEnableQueryAttribute]
public IHttpActionResult Get()
return Ok(_products);
希望这能解决您的问题,谢谢!
风扇。
【讨论】:
这几乎奏效了。它确实改变了服务器上的 $filter 命令,但它也破坏了 $select 命令。应用过滤器时,我不能再使用 $select 了。我收到错误消息“'Product Nullable=True' 类型的 EDM 实例缺少属性 'X'”其中 'X' 是我试图选择的属性名称。如果我在 $select 中包含所有属性,那么它似乎工作正常。 在重新初始化 queryOptions 之前添加“ApplyTo”之后,即使使用 $select 现在也可以完美运行。谢谢。 解决方案在 OData 5.5.1 上对我不起作用。更改 URL 不会影响。 我结束了使用 OnActionExecuting。在此事件中更改了网址,这对我有用。 嘿@afsharm 你能发布一个你的 OnActionExecuting 变体作为解决方案的例子吗?我在支持 StringAsEnumResolver 以及修改 ApplyTo 中的 url 时遇到问题。当我们创建新的 ODataQueryOptions 时,似乎没有正确重新评估自定义 Uri 解析器,我希望 OnActionExecuting 可以解决我的问题。【参考方案2】:作为对@Chris Schaller 的回应,我将自己的解决方案发布如下:
public class CustomEnableQueryAttribute : EnableQueryAttribute
public override void OnActionExecuting(HttpActionContext actionContext)
var url = actionContext.Request.RequestUri.OriginalString;
//change something in original url,
//for example change all A charaters to B charaters,
//consider decoding url using WebUtility.UrlDecode() if necessary
var newUrl = ModifyUrl(url);
actionContext.Request.RequestUri = new Uri(newUrl);
base.OnActionExecuting(actionContext);
【讨论】:
谢谢,您的解决方案不会与 ODataQueryOptions 混淆,这是自 ODataLib v6 以来我的许多问题都出现的地方。如果可行,我将使用 ModifyUrl 示例编辑您的解决方案,该示例通常涉及更改 ODataQueryOptions。 再次感谢@afsharm 我认为在大多数情况下 OnActionExecuting 是强制或修改 OData $filter 参数的最佳位置。这在大多数 OData 查询分析之前执行,因此您无需使用伪造的请求创建新的 ODataQueryOptions 对象。很失望我没有早点发现这一点。 @Afshar- 感谢您的解决方案。我是 OData 的新手。我遵循了您的解决方案,并且像您所做的那样修改了 OnActionExecuting 中的 url。但是在控制器内部,当我看到 queryOptions.Filter.RawValue 的值时,它会显示旧的过滤器值。但是 queryOptions.Request.RequestUri 显示了新修改的 url。你能帮忙吗? @manojmore 我知道这已经很晚了,但是对于任何想要回答您的问题的人来说,方法如下(此代码位于base.OnActionExecuting(actionContext);
之前):var queryOptions = (ODataQueryOptions)actionContext.ActionArguments['options']; queryOptions.Request.RequestUri = newUrl.Uri; var newOdataQueryOptions = (ODataQueryOptions)Activator.CreateInstance(queryOptions.GetType(), queryOptions.Context, queryOptions.Request); actionContext.ActionArguments['options'] = newOdataQueryOptions;
以上是关于OData V4 在服务器端修改 $filter的主要内容,如果未能解决你的问题,请参考以下文章
Web API OData V4 在本地工作,但不在 IIS 上
[转]OData的初步认识 OData v4 Client Code Generator
无法使用 Excel Power Query 连接到 OData v4 服务:元数据无效?