OData V4在服务器端修改$ filter [英] OData V4 modify $filter on server side

查看:294
本文介绍了OData V4在服务器端修改$ filter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想要修改控制器内的过滤器,然后根据更改的过滤器返回数据。



所以因为我在服务器端有一个ODataQueryOptions参数,我可以使用它来查看FilterQueryOption。



我们假设过滤器是这样的$ filter = ID eq -1,但是在服务器端,如果我看到ID为-1,这告诉我用户想要选择所有记录。



我试图将$ filter = ID eq -1更改为$ filter = ID ne -1,这将通过设置Filter.RawValue但这是只读的。

我试图创建一个新的FilterQueryOption,但这需要一个ODataQueryContext和一个ODataQueryOptionParser,我无法弄清楚如何创建。


$ b $然后我尝试设置Filter = Null,然后设置ApplyTo,当我在控制器中设置一个断点,并在立即窗口上检查这一点,但一旦在控制器上留下了GET方法,那么它应该起作用还原回到URL中传递的内容。



这篇文章谈到做一些非常相似的事情修改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());
返回查询为IQueryable< Product> ;;
}

运行此代码不会返回任何产品,因为原始查询在URL想要的产品1和我用产品2交换产品1的ID过滤器。

现在,如果我运行SQL Profiler,我可以看到它添加了一些类似Select * from Product WHERE ID = 1 AND ID = 2。



BUT 如果我尝试相同的东西,替换$ 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());
返回查询为IQueryable< Product> ;;
}

结果

微软的帮助。这是支持过滤器,计数和分页的最终输出。

  using System.Net.Http; 
使用System.Web.OData;
使用System.Web.OData.Extensions;
使用System.Web.OData.Query;

///< summary>
///用于创建自定义过滤器,选择,分组,排序等...
///< / summary>
public class CustomEnableQueryAttribute:EnableQueryAttribute
{
public override IQueryable ApplyQuery(IQueryable queryable,ODataQueryOptions queryOptions)
{
IQueryable result = default(IQueryable);

//在更改前获取原始请求
HttpRequestMessage originalRequest = queryOptions.Request;

//在更改前获取原始URL
string url = originalRequest.RequestUri.AbsoluteUri;

//如果URL包含ID = 0的特定过滤器以重新选择所有记录
if(queryOptions.Filter!= null&& url.Contains( $ filter = ID%20eq%200))
{
//应用新的过滤器
url = url.Replace($ filter = ID%20eq%200,$ filter = ID%20NE%200\" );

//为过滤器构建一个新的请求
HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get,url);

//重新设置新请求的查询选项
queryOptions = new ODataQueryOptions(queryOptions.Context,req);
}

//设置顶级过滤器(如果没有提供)
if(queryOptions.Top == null)
{
//应用查询选项与新的顶部过滤器
result = queryOptions.ApplyTo(可查询,新的ODataQuerySettings {PageSize = 100});
}
else
{
//应用任何以前未应用的待处理信息
result = queryOptions.ApplyTo(可查询);
}

//添加NextLink如果存在
if(queryOptions.Request.ODataProperties()。NextLink!= null)
{
originalRequest .ODataProperties()。NextLink = queryOptions.Request.ODataProperties()。NextLink;
}
//添加TotalCount如果存在
if(queryOptions.Request.ODataProperties()。TotalCount!= null)
{
originalRequest.ODataProperties() .TotalCount = queryOptions.Request.ODataProperties()。TotalCount;
}

//返回所有结果
返回结果;
}
}


解决方案

删除[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(可查询);
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(可查询);
}
}

在控制器方法中使用此属性

  [MyEnableQueryAttribute] 
public IHttpActionResult Get()
{
return Ok (_产品展示);
}

希望这可以解决您的问题,谢谢!



风扇。


I would like to be able to modify the filter inside the controller and then return the data based on the altered filter.

So for I have an ODataQueryOptions parameter on the server side that I can use to look at the FilterQueryOption.

Let's assume the filter is something like this "$filter=ID eq -1" but on the server side if I see "-1" for an ID this tells me that the user wants to select all records.

I tried to change the "$filter=ID eq -1" to "$filter=ID ne -1" which would give me all by setting the Filter.RawValue but this is read only.
I tried to create a new FilterQueryOption but this requires a ODataQueryContext and a ODataQueryOptionParser which I can't figure out how to create.

I then tried to set the Filter = Null and then us the ApplyTo which seems to work when I set a break point in the controller and check this on the immediate window but once it leaves the GET method on the controller then it "reverts" back to what was passed in the URL.

This article talks about doing something very similar "The best way to modify a WebAPI OData QueryOptions.Filter" but once it leaves the controller GET method then it reverts back to the URL query filter.

UPDATE WITH SAMPLE CODE

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

Running this code will not return any product this is because the original query in the URL wanted product 1 and I swapped the ID filter of product 1 with product 2.
Now if I run SQL Profiler, I can see that it added something like "Select * from Product WHERE ID = 1 AND ID = 2".

BUT if I try the same thing by replacing the $top then it works fine.

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

END RESULT
With Microsoft's help. Here is the final output that supports filter, count, and paging.

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

解决方案

Remove [EnableQuery] attribute, your scenario should work, because after using this attribute, OData/WebApi will apply your original query option after you return data in controller, if you already apply in your controller method, then you shouldn't use that attribute.

But if your query option contains $select, those code are not working because the result's type is not Product, we use a wrapper to represent the result of $select, so I suggest you use try this:

Make a customized 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);
    }
}

Use this attribute in your controller method

[MyEnableQueryAttribute]
public IHttpActionResult Get()
{
    return Ok(_products);
}

Hope this can solve your problem, thanks!

Fan.

这篇关于OData V4在服务器端修改$ filter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆