OData V4 изменить фильтр $ на стороне сервера
Я хотел бы иметь возможность изменить фильтр внутри контроллера, а затем вернуть данные на основе измененного фильтра.
Так что у меня есть параметр ODataQueryOptions на стороне сервера, который я могу использовать для просмотра FilterQueryOption.
Давайте предположим, что фильтр похож на этот "$filter=ID eq -1", но на стороне сервера, если я вижу "-1" для идентификатора, это говорит мне о том, что пользователь хочет выбрать все записи.
Я попытался изменить "$filter=ID eq -1" на "$filter=ID ne -1", что дало бы мне все, установив Filter.RawValue, но это только для чтения.
Я пытался создать новый FilterQueryOption, но для этого требуется ODataQueryContext и ODataQueryOptionParser, который я не могу понять, как создать.
Затем я попытался установить Filter = Null, а затем использовать ApplyTo, который, кажется, работает, когда я устанавливаю точку останова в контроллере и проверяю это в ближайшем окне, но как только он оставляет метод GET на контроллере, он "возвращается" назад. на то, что было передано в URL.
В этой статье рассказывается о том, как сделать что-то очень похожее: " Лучший способ изменить ODAP QueryOptions.Filter в WebAPI", но как только он покидает метод 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 на продукт 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;
}
}
2 ответа
Удалите атрибут [EnableQuery], ваш сценарий должен работать, потому что после использования этого атрибута OData/WebApi применит ваш исходный параметр запроса после того, как вы вернете данные в контроллер, если вы уже применили его в методе контроллера, тогда вы не должны использовать этот атрибут,
Но если ваш вариант запроса содержит $select, этот код не работает, потому что тип результата не является Product, мы используем обертку для представления результата $select, поэтому я предлагаю вам использовать try:
Создайте настроенный атрибут 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);
}
}