Asp.net-Web-Api

OData V4 在伺服器端修改 $filter

  • December 31, 2018

我希望能夠修改控制器內部的過濾器,然後根據更改的過濾器返回數據。

因此,對於我在伺服器端有一個 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 中傳遞的內容。

本文討論了做一些非常相似的“修改 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,我可以看到它添加了類似“選擇* 來自產品 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>;
}

結束結果

在 Microsoft 的幫助下。這是支持過濾、計數和分頁的最終輸出。

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

希望這能解決您的問題,謝謝!

Fan.

引用自:https://stackoverflow.com/questions/33660648