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

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

问题描述

我希望能够修改控制器内部的过滤器,然后根据更改后的过滤器返回数据.

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

因此,我在服务器端有一个 ODataQueryOptions 参数,我可以用它来查看 FilterQueryOption.

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

让我们假设过滤器类似于$filter=ID eq -1",但在服务器端,如果我看到 ID 为-1",这告诉我用户想要选择所有记录.

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.

我尝试将 "$filter=ID eq -1" 更改为 "$filter=ID ne -1",这将通过设置 Filter.RawValue 为我提供所有内容,但这是只读的.
我试图创建一个新的 FilterQueryOption,但这需要一个 ODataQueryContext 和一个 ODataQueryOptionParser,我不知道如何创建.

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.

然后我尝试设置 Filter = Null 然后我们设置 ApplyTo 当我在控制器中设置断点并在立即窗口上检查它时似乎工作但一旦它离开控制器上的 GET 方法然后它恢复返回到 URL 中传递的内容.

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.

本文讨论了做一些非常相似的事情修改 WebAPI OData QueryOptions.Filter 的最佳方法",但是一旦它离开控制器 GET 方法,它就会恢复到 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.

使用示例代码更新

[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"的内容.

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".

但是 如果我通过替换 $top 来尝试同样的事情,那么它工作正常.

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

推荐答案

移除 [EnableQuery] 属性,您的场景应该可以工作,因为使用此属性后,OData/WebApi 将在您在控制器中返回数据后应用您的原始查询选项,如果你已经在你的控制器方法中应用了,那么你不应该使用那个属性.

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.

但是如果您的查询选项包含 $select,这些代码将不起作用,因为结果的类型不是 Product,我们使用包装器来表示 $select 的结果,所以我建议您使用 try this:

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:

制作自定义的 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);
}

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

Hope this can solve your problem, thanks!

风扇.

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

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