ASP .NET MVC 4 WebApi:手动处理OData查询
我有一个使用ASP.NET MVC 4提供的WebAPI制作的Web服务。我知道WebAPI工作的顶层自动处理OData查询 (例如$filter
, $top
, $skip
),但是如果我想自己处理过滤?
我不只是从我的数据库返回数据 ,但我有另一个层添加一些属性,进行一些转换等。所以查询我的所有数据,转换他们,并返回到WebAPI类OData过滤不仅仅是好足够。 这当然是非常缓慢,通常是一个糟糕的主意。
那么是否有一种方法可以将我的WebAPI入口点的OData查询参数传播到我调用的函数来获取和转换数据?
例如,GET to /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; }
我只是偶然发现了这个旧的post,我现在添加这个答案,因为它现在很容易处理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; } }
来自URL的查询被转换成LINQexpression式树,然后针对您的操作返回的IQueryable执行。 您可以分析expression式并以任何您想要的方式提供结果。 缺点是你需要实现IQueryable,这不是太容易。 看看这个博客系列文章,如果你有兴趣: http : //blogs.msdn.com/b/vitek/archive/2010/02/25/data-services-expressions-part-1-intro.aspx 。 它讨论了WCF数据服务,但Web API使用的filterexpression式将非常相似。
一种方式与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); }
我用WCF数据服务和asp.net mvc 3.5做了这样的事情,但是有点混乱。
第一步是重写path,使skip和top选项不会被应用两次,一次由你和一次由运行时。
我用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); } }
然后只要检查查询string,并拔出你需要的参数。
if (HttpContext.Current.Request.QueryString["filter"] != null) var filter = HttpContext.Current.Request.QueryString["filter"] as string;
然后拆分filterstring,并将其parsing为一个sql语句或任何其他数据库命令。 我使用的是NHibernate。 我也能够限制哪些filter命令,我支持使事情变得更容易。 例如,我没有做分组。 大多只是比较运算符。
OData.org上有一个filter运算符列表。 将string“and”和“or”拆分成单独的子句。 将每个子句拆分一个空格,您应该得到一个3元素数组,其属性名称分别为[0]中的[1]中的运算符和[2]中的值。
这些条款将是一个人的条款,但我假设你将能够将它们转换成将从数据库中获得正确结果的东西。
我最终放弃了这种方法,因为它不适用于POSTS。 我必须编写我自己的Linq提供程序,但是编写我自己的filterstring分析器使其更易于理解。