简体   繁体   English

ASP.NET Web Api复杂的查询参数

[英]ASP.NET Web Api complex query parameters

I have a Web Api endpoint which is currently called like this: 我有一个Web Api端点,目前这样称为:

http://api.example.com/scenes?creationDate=1440091949

I am trying to support more complex queries such as: 我正在尝试支持更复杂的查询,例如:

http://api.example.com/scenes?creationDate.lt=1440091949

Note the .lt suffix. 注意.lt后缀。 This would let the user to list all the scenes in which creationDate is less than (lt) than 1440091949 . 这将允许用户列出creationDate 小于 (lt)而不是1440091949所有场景。

For this, I had to create my own mapper function which would map each query parameter to a query model's properties and store each query operation (lt, gt, eq, etc...) in a dictionary of operations: 为此,我必须创建自己的映射器函数,它将每个查询参数映射到查询模型的属性,并将每个查询操作(lt,gt,eq等等)存储在操作字典中:

protected QueryData MapToQueryData(IEnumerable<KeyValuePair<string, string>> queryParameters)
{
     QueryData queryData = new QueryData();

     foreach(var queryParam in queryParameters)
     {
          string[] segments = queryParam.Key.Split('.'); // ["creationDate", "lt"]
          if(!segments.Any()) continue;

          PropertyInfo queryProperty = _reflection.Properties.GetProperty<QueryData>(segments.First()); // "CreationDate" Property
          if(queryProperty == null) continue;

          object value = _reflection.Conversion.ConvertTo(queryParam.Value, property.PropertyType); // Convert 1440091949 to long
          if(value == null) continue;

          _reflection.Properties.SetPropertyValue(queryData, property, value); // Set queryData.CreationDate = 1440091949

          if(segments.Length < 2) continue;

          PropertyInfo mapProperty = _reflection.Properties.GetPropertiesWithAttribute<QueryData, OperationMapAttribute>().FirstOrDefault(); // Identify Property annotated with [OperationMap]
          if(mapProperty == null) continue;

          Dictionary<string, string> operationMap = _reflection.Properties.GetPropertyValue(queryData, mapProperty) as Dictionary<string, string>(); // Get dictionary from Map property
          if(operationMap == null) continue;

          if(!operationMap.ContainsKey(property.Name))
                 operationMap.Add(property.Name, segments.Last()); // "creationDate" -> "lt"
          else
                 operationMap[property.Name] = segments.Last();

          _reflection.Properties.SetPropertyValue(queryData, mapProperty, operationMap); // Set Map property of QueryData to the updated dictionary
     }

     return queryData;
}

I know there exists an automatic mapping provided by ASP.NET Web Api if one decides to use the [FromUri] attribute, but that would work if I had simple query parameters such as creationDate=1440091949 , but will not work if I send a query parameter such as creationDate.lt=1440091949 . 我知道如果一个人决定使用[FromUri]属性,ASP.NET Web Api提供了一个自动映射,但是如果我有简单的查询参数,例如creationDate=1440091949 ,那将会creationDate=1440091949 ,但如果我发送一个查询,它将不起作用参数如creationDate.lt=1440091949

Is there something built in, in the Web APi engine to handle these types of query parameters? 在Web APi引擎中是否有内置的东西来处理这些类型的查询参数? I have seen them on a lot of web services, so they are pretty common for ordering or doing complex queries. 我在很多Web服务上看过它们,所以它们在订购或执行复杂查询时非常常见。

Have you tried using something like the OData query syntax? 您是否尝试过使用OData查询语法之类的东西?

http://blogs.msdn.com/b/martinkearn/archive/2015/03/10/using-odata-query-syntax-with-web-api.aspx http://blogs.msdn.com/b/martinkearn/archive/2015/03/10/using-odata-query-syntax-with-web-api.aspx

From the aforementioned... 从上述......

What is OData query syntax? 什么是OData查询语法?

OData query syntax is a standard way of querying RESTful APIs using a pre-defined syntax that allows the calling client to define the sort order, filter parameters and pages of data that is returned. OData查询语法是使用预定义语法查询RESTful API的标准方法,该语法允许调用客户端定义排序顺序,过滤参数和返回的数据页面。

The full syntax can be found in section 5 of the OData Version 4.0 Part 2 specification but here are some quick examples of how the syntax is used: 完整语法可以在OData Version 4.0 Part 2规范的第5节中找到,但这里有一些如何使用语法的简单示例:

  • .../host/service/Products?$filter=Name eq 'Milk' : Returns all products where the name is equal to 'Milk' ... / host / service / Products?$ filter = Name eq'Milk' :返回名称等于'Milk'的所有产品
  • .../host/service/Products?$filter=Name eq 'Milk' and Price lt 2.55 : Returns all products where the name is equal to 'Milk' and the price is less than 2.55 ... / host / service / Products?$ filter = Name eq'Milk'和Price lt 2.55 :返回名称等于'Milk'并且价格低于2.55的所有产品
  • .../host/service/Products?$top=10&$skip10 : Returns items 11>21. ... / host / service / Products?$ top = 10&$ skip10 :返回项目11> 21。 The $top system query option requests the number of items in the queried collection to be included in the result. $ top系统查询选项请求查询的集合中的项目数包含在结果中。 The $skip query option requests the number of items in the queried collection that are to be skipped and not included in the result $ skip查询选项请求查询集合中要跳过但未包含在结果中的项目数
  • .../host/service/Products?$orderby=Name desc,Id : Returns all products ordered by the Name in descending order, then ordered by Id ... / host / service / Products?$ orderby = Name desc,Id :按降序返回按名称排序的所有产品,然后按ID排序
  • .../host/service/Products?$filter=Enabled eq true : Returns all products where the Enabled field (which is a boolean) is true ... / host / service / Products?$ filter = Enabled eq true :返回Enabled字段(布尔值)为true的所有产品
  • .../host/service/Products?$filter=substringof('Martin', Name) : Returns all products the Name field contains the text Martin ... / host / service / Products?$ filter = substringof('Martin',Name) :返回名称字段包含文本Martin的所有产品

How to use OData query syntax in ASP.net Web API? 如何在ASP.net Web API中使用OData查询语法?

This is where things get really awesome - it is super, super easy to change a regular Web API controller to support OData query syntax - once you know how to do this, you'll never not do it! 事情变得非常棒 - 超级,非常容易更改常规Web API控制器以支持OData查询语法 - 一旦你知道如何做到这一点,你就永远不会这样做!

If we assume we are starting from a regular Web API project (File > New Project > ASP.net Web Application > Web API), you first need to add a scaffolded controller which you can do by following these steps: 如果我们假设我们从常规Web API项目(文件>新建项目> ASP.net Web应用程序> Web API)开始,您首先需要添加一个脚手架控制器,您可以按照以下步骤操作:

  1. Add a model class (I'm using the typical 'Person' class with Id, First Name, Last Name, Age etc) 添加一个模型类(我使用典型的'Person'类,包括Id,名字,姓氏,年龄等)
  2. Right-click the 'controllers' folder > Add > New Scaffolded item > Web API 2 Controller with actions, using Entity Framework > Model = Person 使用Entity Framework> Model = Person右键单击'controllers'文件夹> Add> New Scaffolded item> Web API 2 Controller with actions

NOTE: You do not need to choose the 'Web API 2 OData Controller...' option, you can add the query syntax to any controller 注意:您无需选择“Web API 2 OData Controller ...”选项,您可以将查询语法添加到任何控制器

Once you've setup your controller, you should end up with a simple controller which has a default GET action that looks a little like this: 一旦你设置了你的控制器,你应该得到一个简单的控制器,它有一个默认的GET动作,看起来有点像这样:

// GET: api/People
public IQueryable<Person> GetPeople()
{
    return db.People;
}

This action will simply return all the rows in the 'People' table of the database with no ability to filter, sort etc. To add full OData query support, you need to make a few changes: 此操作将简单地返回数据库“人员”表中的所有行,无法进行过滤,排序等。要添加完整的OData查询支持,您需要进行一些更改:

  1. Add the Microsoft.Aspnet.OData package via nugget ... simply pop this into you 'Package Manager Console': Install-Package Microsoft.AspNet.Odata 通过nugget添加Microsoft.Aspnet.OData包...只需将其弹出到“包管理器控制台”中:Install-Package Microsoft.AspNet.Odata
  2. Add this using statement: using System.Web.OData; 使用以下语句添加:使用System.Web.OData;
  3. Add the[EnableQueryAttribute]attribute to your GetPeople action. 将[EnableQueryAttribute]属性添加到GetPeople操作中。
  4. Add AsQueryable(); 添加AsQueryable(); to your return line so it looks like this: return db.People.AsQueryable(); 到你的返回行所以它看起来像这样:return db.People.AsQueryable();

Your finished code will look something like this: 您完成的代码将如下所示:

// GET: api/People
[EnableQueryAttribute]
public IQueryable<Person> GetPeople()
{
    return db.People.AsQueryable();
}

That is all there is to it! 这就是它的全部! .... now you have this setup, you can use the full OData syntax to query sort etc without any extra code. ....现在你有了这个设置,你可以使用完整的OData语法来查询排序等,而无需任何额外的代码。

You could put the operator into the parameter value: 您可以将运算符放入参数值:

http://api.example.com/scenes?creationDateTest=lt.1440091949

then test the beginning part of the string you get for the creationDate parameter to see if it contains a valid operator. 然后测试您为creationDate参数获取的字符串的开头部分,以查看它是否包含有效的运算符。 Only be careful, I would still parameterize the actual value. 只是要小心,我仍然会参数化实际值。 So convert this into 所以把它转换成

string cmd = "select .... from ... where creationdate < @creationDate";

and feed in the 1440091949 as parameter @creationDate. 并在1440091949中输入参数@creationDate。

I just tried this on one of my web forms, and it works fine. 我只是在我的一个网络表单上试过这个,它运行正常。 You get those parameters as strings anyway, so it works, and it does not even feel like a kludge. 无论如何,你将这些参数作为字符串获取,因此它可以工作,甚至不像一个kludge。 And I am oversensitive to anything that smells of kludge. 而且我对那些闻起来有点混乱的东西过于敏感。

PS: notice how I changed the name of the parameter in the query string from creationDate to creationDate Test . PS:注意我是如何将查询字符串中的参数名称从creationDate更改为creationDate Test

Will the life on earth end if you have it this way? 如果你有这样的话,地球上的生命会结束吗?

http://api.example.com/scenes?cd=1440091949&cdop=lt&ep=45&epop=gt
http://api.example.com/scenes?cd=1440091949&cdop=gt&ep=45&epop=lt
http://api.example.com/scenes?cd=1440091949&cdop=eq&ep=45&epop=eq

No complicated mapping required in this case. 在这种情况下,不需要复杂的映射。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM