zoukankan      html  css  js  c++  java
  • [翻译]在ASP.NET Web API中通过OData支持查询和分页

    OData可以通过形如http://localhost/Products?$orderby=Name这样的QueryString传递查询条件、排序等。你可以在任何Web API Controller中启用OData查询条件,并且不需要让Controller设置为OData的终结点(EndPoint),以便支持过滤、排序。

    启用查询选项前,你应该看下这篇文档——OData安全指导.

     

    启用OData查询选项

    Web API支持下列的查询选项:

    选项 说明
    $expand 展开关联的内嵌实体
    $filter 通过等式过滤查询条件(请注意:查询时区分大小写的)
    $inlinecount 告诉服务器需要返回的记录数(服务器分页)。
    $orderby 排序
    $select 要选择的属性字段
    $skip 跳过N条记录
    $top 只返回最开始的N条记录

    要使用OData查询条件,需要显示的启用。可以对整个Web应用程序进行配置,或者只指定特定的Controller或者Action。

    全局配置,可以在global.ascx中修改,这样任意一个返回类型为IQueryable的方法都会被匹配。

     
       1:  public static class WebApiConfig
       2:      {
       3:          public static void Register(HttpConfiguration config)
       4:          {
       5:              config.Routes.MapHttpRoute(
       6:                  name: "DefaultApi",
       7:                  routeTemplate: "api/{controller}/{id}",
       8:                  defaults: new { id = RouteParameter.Optional }
       9:              );
      10:             
      11:             config.Formatters.JsonFormatter.AddQueryStringMapping("$format", "json", "application/json"); 
      12:   
      13:             config.Formatters.XmlFormatter.AddQueryStringMapping("$format", "xml", "application/xml");
      14:   
      15:              config.EnableQuerySupport();
      16:          }

    如果不需要这么处理,可以在方法或者Controller上标识。

       1:  public class ProductsController : ApiController
       2:  {
       3:      [Queryable]
       4:      IQueryable<Product> Get() {}
       5:  }

    查询范例

    客户端驱动的分页

    对于一个大数据集,客户端每次希望返回10条记录,在点击“Next”时获取下一页。请求的URI如下:

    http://localhost/Products?$top=10&$skip=20

    过滤

    查询选项就是通过等式进行匹配,可以包含逻辑和算术操作符,String和时间函数。

    返回Category为Toys的产品 http://localhost/Products?$filter=Category eq 'Toys'
    返回所有价格小于10的产品列表 http://localhost/Products?$filter=Price lt 10
    返回价格在5到15之间的所有产品列表 http://localhost/Products?$filter=Price ge 5 and Price le 15
    返回名称中包含zz的产品列表 http://localhost/Products?$filter=substringof('zz',Name)
    返回2005年以后发布的产品列表 http://localhost/Products?$filter=year(ReleaseDate) gt 2005

    排序

    对数据进行排序。

    按价格排序 http://localhost/Products?$orderby=Price
    按价格降序排列 http://localhost/Products?$orderby=Price desc
    先按照category排序,然后根据价格降序排列 http://localhost/odata/Products?$orderby=Category,Price desc

    服务器端驱动分页

    有时候为了对API接口进行访问,可以主动在服务器端进行分页限制。要启用服务器端分页,在Queryable属性中添加参数PageSize。

       1:  [Queryable(PageSize=10)]
       2:  public IQueryable<Product> Get() 
       3:  {
       4:      return products.AsQueryable();
       5:  }

    经过上述设置以后,返回的消息体中应该类似于下面。其中包含了nextpage的链接,客户端可以通过该链接获取下一页。

       1:  {
       2:    "odata.metadata":"http://localhost/$metadata#Products",
       3:    "value":[
       4:      { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
       5:      { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
       6:      // Others not shown
       7:    ],
       8:    "odata.nextLink":"http://localhost/Products?$skip=10"
       9:  }

    如果希望得到数据库的总记录数,那么应该包含$inlinecount条件,类似于这样http://localhost/Products?$inlinecount=allpages

       1:  {
       2:    "odata.metadata":"http://localhost/$metadata#Products",
       3:    "odata.count":"50",
       4:    "value":[
       5:      { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
       6:      { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
       7:      // Others not shown
       8:    ]
       9:  }

    下一页链接和总记录数都需要使用OData格式。对于不是此种格式,则需要将查询结果包装在一个PageResult<T>对象中。因此需要多增加一些代码。

       1:  public PageResult<Product> Get(ODataQueryOptions<Product> options)
       2:  {
       3:      ODataQuerySettings settings = new ODataQuerySettings()
       4:      {
       5:          PageSize = 5
       6:      };
       7:   
       8:      IQueryable results = options.ApplyTo(_products.AsQueryable(), settings);
       9:   
      10:      return new PageResult<Product>(
      11:          results as IEnumerable<Product>, 
      12:          Request.GetNextPageLink(), 
      13:          Request.GetInlineCount());
      14:  }

    返回结果应该如下:

       1:  {
       2:    "Items": [
       3:      { "ID":1,"Name":"Hat","Price":"15","Category":"Apparel" },
       4:      { "ID":2,"Name":"Socks","Price":"5","Category":"Apparel" },
       5:   
       6:      // Others not shown
       7:      
       8:    ],
       9:    "NextPageLink": "http://localhost/api/values?$inlinecount=allpages&$skip=10",
      10:    "Count": 50
      11:  }

    限制查询条件

    查询选项给予客户端对于服务器端查询的掌控入口,在一些条件下,处于安全和性能的理由,我们希望对可用的选项进行限制,此时需要利用Queryable属性进行处理。如果只允许使用skip和top,选项设置如下:

       1:  [Queryable(AllowedQueryOptions= AllowedQueryOptions.Skip | AllowedQueryOptions.Top)]

    如果要对排序字段进行限制(如非排序字段),应该如下:

       1:  [Queryable(AllowedOrderByProperties="Id")] //英文逗号分隔

    在过滤条件中,只允许使用eq操作符,格式是这样:

       1:  [Queryable(AllowedLogicalOperators=AllowedLogicalOperators.Equal)]

    不允许启用算术操作符,则应该如下:

       1:  [Queryable(AllowedArithmeticOperators=AllowedArithmeticOperators.None)]

    同样,这种限制还可以对全局网站应用程序启用。

       1:  var queryAttribute = new QueryableAttribute()
       2:  {
       3:      AllowedQueryOptions = AllowedQueryOptions.Top | AllowedQueryOptions.Skip,
       4:      MaxTop = 100
       5:  };
       6:                  
       7:  config.EnableQuerySupport(queryAttribute);

    直接调用查询选项

    如果不使用Queryable标签,你还可以直接在Controller中调用查询选项。

       1:  public IQueryable<Product> Get(ODataQueryOptions opts)
       2:  {
       3:      var settings = new ODataValidationSettings()
       4:      {
       5:          // Initialize settings as needed.
       6:          AllowedFunctions = AllowedFunctions.AllMathFunctions
       7:      };
       8:   
       9:      opts.Validate(settings);
      10:   
      11:      IQueryable results = opts.ApplyTo(products.AsQueryable());
      12:      return results as IQueryable<Product>;
      13:  }

    Web API通过URI查询条件来操作ODataQueryOptions,将一个IQueryable对象传递给ApplyTo方法,该方法返回另一个IQueryable。

    对于复杂的应用场景,如果没有IQueryable查询,可以检查ODataQueryOptions并且将查询选项转换为另一种形式(例如RaghuRam Nadiminti的文章《将OData查询转换为HQL》)。

    查询验证

    Queryable属性在执行查询前会进行验证,实际上通过QueryableAttribute.ValidateQuery进行,你也可以对此进行自定义(可以参见OData安全指导)。

       1:  public class MyOrderByValidator : OrderByQueryValidator
       2:  {
       3:      // Disallow the 'desc' parameter for $orderby option.
       4:      public override void Validate(OrderByQueryOption orderByOption,
       5:                                      ODataValidationSettings validationSettings)
       6:      {
       7:          if (orderByOption.OrderByNodes.Any(
       8:                  node => node.Direction == OrderByDirection.Descending))
       9:          {
      10:              throw new ODataException("The 'desc' option is not supported.");
      11:          }
      12:          base.Validate(orderByOption, validationSettings);
      13:      }
      14:  }

    还有一种方法,也可以对Queryable属性进行扩展。

       1:  public class MyQueryableAttribute : QueryableAttribute
       2:  {
       3:      public override void ValidateQuery(HttpRequestMessage request, 
       4:          ODataQueryOptions queryOptions)
       5:      {
       6:          if (queryOptions.OrderBy != null)
       7:          {
       8:              queryOptions.OrderBy.Validator = new MyOrderByValidator();
       9:          }
      10:          base.ValidateQuery(request, queryOptions);
      11:      }
      12:  }

    然后再global.ascx中或者controller、action上使用。

       1:  // Globally:
       2:  config.EnableQuerySupport(new MyQueryableAttribute());
       3:   
       4:  // Per controller:
       5:  public class ValuesController : ApiController
       6:  {
       7:      [MyQueryable]
       8:      public IQueryable<Product> Get()
       9:      {
      10:          return products.AsQueryable();
      11:      }
      12:  }

    如果直接使用了ODataQueryOptions,可以这样处理。

       1:  public IQueryable<Product> Get(ODataQueryOptions opts)
       2:  {
       3:      if (opts.OrderBy != null)
       4:      {
       5:          opts.OrderBy.Validator = new MyOrderByValidator();
       6:      }
       7:   
       8:      var settings = new ODataValidationSettings()
       9:      {
      10:          // Initialize settings as needed.
      11:          AllowedFunctions = AllowedFunctions.AllMathFunctions
      12:      };
      13:   
      14:      // Validate
      15:      opts.Validate(settings);
      16:   
      17:      IQueryable results = opts.ApplyTo(products.AsQueryable());
      18:      return results as IQueryable<Product>;
      19:  }

    来源:http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options

    作者:Mike Wasson(微软科技作家)

  • 相关阅读:
    关于89S51单片机数码管显示的小工具
    重新启动IIS的小工具
    有没有朋友做过动态表结构的,请教一下
    使用PPC的朋友慎用 星空极速3.2.070416_GD
    自定义app_offline.htm,让网站升级提示更专业
    2010全面兼容IE6/IE7/IE8/FF的CSS HACK写法
    CSS Sprites
    自适应宽度圆角按钮,导航,用到了滑动门技术
    设置frameset的高度
    jquey checkbox全选,反选
  • 原文地址:https://www.cnblogs.com/tukzer/p/3669418.html
Copyright © 2011-2022 走看看