OData V4在服务器端修改$filter



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

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

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

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

然后,我尝试将Filter设置为Null,然后将ApplyTo设置为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());
    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;
    }
}

删除[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);
}

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

风扇。

作为@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);
    }
}

最新更新