Подтвердить что ты не робот

ASP.NET MVC 4 WebApi: вручную обрабатывать запросы OData

У меня есть веб-служба, созданная с использованием WebAPI, предоставляемой ASP.NET MVC 4. Я знаю, что слой, поверх которого работает WebAPI, автоматически обрабатывает OData Queries (например, $filter, $top, $skip), но что, если я хочу самостоятельно обработать фильтрацию?

Я не просто возвращаю данные из своей базы данных, но у меня есть еще один слой, который добавляет некоторые свойства, делает некоторые преобразования и т.д. Так что запрос Все мои данные, конвертируя их и возвращая их в класс WebAPI для фильтрации OData, не только достаточно хороши. Это, конечно, ужасно медленно, и в целом дрянной идеей.

Итак, существует ли способ распространять параметры запроса OData​​strong > из моей точки входа WebAPI в функции, которые я вызываю для получения и преобразования данных?

Например, GET на /api/people?$skip=10&$top=10 будет вызывать на сервере:

public IQueryable<Person> get() {
    return PersonService.get(SomethingAboutCurrentRequest.CurrentOData);
}

И в PersonService:

public IQueryable<Person> getPeople(var ODataQueries) {
    IQueryable<ServerSidePerson> serverPeople = from p in dbContext.ServerSidePerson select p;
    // Make the OData queries
    // Skip
    serverPeople = serverPeople.Skip(ODataQueries.Skip);
    // Take
    serverPeople = serverPeople.Take(ODataQueries.Take);
    // And so on
    // ...

    // Then, convert them
    IQueryable<Person> people = Converter.convertPersonList(serverPeople);
    return people;
}
4b9b3361

Ответ 1

Я просто наткнулся на этот старый пост, и я добавляю этот ответ, поскольку теперь очень легко обрабатывать запросы OData. Вот пример:

[HttpGet]
[ActionName("Example")]
public IEnumerable<Poco> GetExample(ODataQueryOptions<Poco> queryOptions)
{
    var data = new Poco[] { 
        new Poco() { id = 1, name = "one", type = "a" },
        new Poco() { id = 2, name = "two", type = "b" },
        new Poco() { id = 3, name = "three", type = "c" }
    };

    var t = new ODataValidationSettings() { MaxTop = 2 };
    queryOptions.Validate(t);

    //this is the method to filter using the OData framework
    //var s = new ODataQuerySettings() { PageSize = 1 };
    //var results = queryOptions.ApplyTo(data.AsQueryable(), s) as IEnumerable<Poco>;

    //or DIY
    var results = data;
    if (queryOptions.Skip != null) 
        results = results.Skip(queryOptions.Skip.Value);
    if (queryOptions.Top != null)
        results = results.Take(queryOptions.Top.Value);

    return results;
}

public class Poco
{
    public int id { get; set; }
    public string name { get; set; }
    public string type { get; set; }
}

Ответ 2

Запрос из URL-адреса преобразуется в дерево выражений LINQ, которое затем выполняется против IQueryable, возвращаемого вашей операцией. Вы можете анализировать выражение и предоставлять результаты любым способом. Недостатком является то, что вам нужно реализовать IQueryable, который не является очень простым. Взгляните на эту серию блога, если вам интересно: http://blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx. В нем рассказывается о службах данных WCF, но выражения фильтра, используемые веб-API, будут очень похожими.

Ответ 3

Один из способов использования Web-api будет с обработчиком сообщений клиента http://www.asp.net/web-api/overview/working-with-http/http-message-handlers

Напишите собственный обработчик, как показано ниже:

public class CustomHandler : DelegatingHandler
    {
        protected override Task<HttpResponseMessage> SendAsync(
            HttpRequestMessage request, CancellationToken cancellationToken)
        {
            return base.SendAsync(request, cancellationToken).ContinueWith(
                (task) =>
                {
                    HttpResponseMessage response = task.Result;
                    var persons = response.Content.ReadAsAsync<IQueryable<Person>>().Result;
                    var persons2 = new List<Person>(); //This can be the modified model completely different
                    foreach (var item in persons)
                    {
                        item.Name = "changed"; // here you can change the data
                        //persons2.Add(....); //Depending on the results modify this custom model
                    }
                    //overwrite the response
                    response = new HttpResponseMessage<IEnumerable<Person>>(persons2); 
                    return response;
                }
            );
        }
    }

Зарегистрируйтесь в global.asax.cs

Метод в классе приложения:

static void Configure(HttpConfiguration config)
 {
     config.MessageHandlers.Add(new CustomHandler()); 
 }

protected void Application_Start()
{
     ....
     .....
     //call the configure method
     Configure(GlobalConfiguration.Configuration);
 }

Ответ 4

Я сделал что-то подобное с службами данных WCF и asp.net mvc 3.5, но это было немного клочья.

Первым шагом является переписать путь так, чтобы параметры пропуска и вершины не применялись дважды, один раз вами и один раз во время выполнения.

Я переписал HttpModule. В методе BeginRequest у вас будет такой код:

HttpApplication app = (HttpApplication)sender;
if (HttpContext.Current.Request.Path.Contains(YOUR_SVC))
{
    if (app.Request.Url.Query.Length > 0)
    {
        //skip questionmark
        string queryString = app.Request.Url.Query.Substring(1) 
                    .Replace("$filter=", "filter=")
                    .Replace("$orderby=", "orderby=")
                    .Replace("$top=", "top=")
                    .Replace("$skip=", "skip=");

                HttpContext.Current.RewritePath(app.Request.Path, "", queryString);
    }
}

Затем просто просмотрите строку запроса и вырвите нужные параметры.

if (HttpContext.Current.Request.QueryString["filter"] != null)
    var filter = HttpContext.Current.Request.QueryString["filter"] as string;

Затем разделите строку фильтра и проанализируйте ее в инструкции sql или любых других командах db. Я использовал NHibernate в моем случае. Я также смог ограничить, какие команды фильтра я поддерживал, что упростило ситуацию. Например, я не делал группировки. В основном просто операторы сравнения.

Там есть список операторов фильтра в OData.org. Разделите строку на "и" и "или" на отдельные предложения. Разделите каждое предложение пробелом, и вы должны получить 3-элементный массив с именем свойства в [0] оператором в [1] и значением в [2].

Статьи будут в терминах Лица, но я предполагаю, что вы сможете преобразовать их во что-то, что выведет правильные результаты из db.

В итоге я отказался от этого подхода, поскольку он не будет работать для POSTS. Мне пришлось написать собственный провайдер Linq, но я написал свой собственный синтаксический анализатор фильтров, чтобы это было легче понять.