RESTful日#9:ASP中的OData。净Web api

表的内容 目录介绍路线图odata查询选项设置方案odata端点 $top $过滤器$orderby $orderby与$top $skip 标准过滤器操作符标准查询函数分页查询选项约束 allowedqueryoptions allowedorderbyproperties allowedlogicaloperator 结论引用 介绍 这是RESTful系列的最后一篇文章,在这篇文章中,我将解释如何在ASP中利用OData功能。净之前。我将解释OData是什么,我们将创建支持OData的RESTful服务。我将尽量使这篇文章非常简洁,少一些理论,多一些实际的实现。 路线图 下面是我逐步学习WebAPI的路线图: RESTful日#1:企业级应用程序架构,使用实体框架、通用存储库模式和工作单元的Web api。RESTful日#2:使用Unity容器和引导程序在Web api中使用依赖注入实现控制反转。RESTful日#3:在ASP中使用控制反转和依赖注入解决依赖关系的依赖关系。NET Web api与Unity容器和管理扩展框架(MEF)。RESTful日#4:使用MVC 4 Web api中的属性路由自定义URL重写/路由。RESTful日#5:使用操作过滤器的Web api中基于基本身份验证和令牌的自定义授权。RESTful日#6:使用操作过滤器、异常过滤器和NLog在Web api中进行请求日志记录和异常处理/日志记录。RESTful日#7:使用NUnit和Moq框架在WebAPI中进行单元测试和集成测试(第一部分)。使用NUnit和Moq框架在WebAPI中进行单元测试和集成测试(第二部分)。净Web api。RESTful日#10:创建自托管的ASP。NET WebAPI与CRUD操作在Visual Studio 2010 我有意使用Visual Studio 2010和。net Framework 4.0,因为在。net Framework 4.0中有一些很难找到的实现,但我将通过演示如何实现来简化它。 OData OData是一个协议,它提供了创建可查询REST服务的灵活性。它提供了某些查询选项,通过这些查询选项,客户机可以通过HTTP从服务器获取随需应变数据。 以下是来自ASP.NET的定义: 开放数据协议(OData)是一个网络数据访问协议。OData提供了通过CRUD操作(创建、读取、更新和删除)查询和操作数据集的统一方法。 更多的阐述: "OData定义了可以用来修改OData查询的参数。客户端以请求URI的查询字符串的形式发送这些参数。例如,要对结果进行排序,客户机使用$orderby参数: http://localhost/Products? $ orderby =名字 OData规范调用这些参数查询选项。您可以为项目中的任何Web API控制器启用OData查询选项——控制器不需要是OData端点。这为您提供了一种方便的方式来添加功能,如过滤和排序到任何Web API应用程序。” 假设数据库中的product表包含超过50000种产品,我们希望根据产品id、价格或名称等特定条件只获取前50种产品。根据我们当前的服务实现,我必须从服务器数据库获取所有产品,并在客户机上过滤它们。另一种选择是,我只能在服务器上获取数据,过滤相同的数据,并发送过滤的数据到客户端。在这两种情况下,我都承担了编写额外代码过滤数据的成本。这就是OData的作用。OData允许你创建可查询的服务。如果公开服务的端点启用了OData,或者支持OData查询选项,那么服务实现将考虑OData请求并相应地处理它。因此,如果对50条记录的请求是一个OData请求,服务将只从服务器获取50条记录。不仅是过滤,OData还提供搜索、排序、跳过数据和选择数据等功能。我将用实际的实现来解释这个概念。我们将使用我们已经创建的服务,并修改它们以启用OData查询选项。 查询选项 以下是用于ASP的OData查询选项。NET WebAPI支持: $orderby:按照升序或降序等特定顺序对获取的记录进行排序。$select:选择结果集中的列或属性。指定在获取的结果中包含哪些属性。$skip:用于跳过记录或结果的数量。例如,我想在获取完整表数据时跳过数据库中的前100条记录,然后我可以使用$skip。$top:只取前n条记录。例如,我想从数据库中获取前10条记录,然后是我的特定服务应该启用OData来支持$top查询选项。$expand:扩展已获取实体的相关域实体。$filter:根据特定条件过滤结果集,类似于LINQ的where子句。例如,我想获取50个成绩超过90%的学生的记录,然后我就可以使用这个查询选项。$inlinecount:这个查询选项主要用于客户端分页。它告诉从服务器获取到客户机的实体总数。 设置解决方案 当你从我上一篇文章中取出代码库并在Visual Studio中打开它时,你会看到项目结构如下图所示: 该解决方案包含WebAPI应用程序和相关项目。 Step1:点击Tools->库包管理器→包管理器控制台 Step2:在包管理器控制台中,选择默认项目作为WebApi,并运行命令:Install-Package Microsoft.AspNet.WebApi。OData - version 4.0.0 注意,因为我们使用的是VS 2010和。net framework 4.0,所以我们需要安装OData库来与之兼容。 该命令将下载一些依赖包,并在项目引用中引用DLL。你会在你的项目引用中得到OData引用DLL。 我们的项目设置为OData端点。您可以创建新的服务。我将修改现有的服务来演示OData的工作。 OData端点 在WebAPI项目中打开ProductController类并转到Get()方法。此方法从数据库中获取所有产品记录。代码如下: 隐藏,复制Code

[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities);
    throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
}

让我们通过测试客户机运行代码。只要运行应用程序,我们得到: 在URL中添加/帮助并按enter,您将看到测试客户端。 由于我们的产品控制器是安全的,所以我们需要从服务获得一个经过身份验证的令牌,并使用它来访问产品控制器方法。要了解WebAPI安全性,请参阅本文。单击POST authenticate API方法,获取测试客户端的TestAPI页面。 现在让我们发送带有凭据的请求。只需在请求中添加标题。标题应该是: 授权:Basic YWtoaWw6YWtoaWw= 这里“YWtoaWw6YWtoaWw=”是我的Base64编码的用户名和密码在数据库,即akhil:akhil 如果经过授权,您将获得一个令牌。保存这个令牌,以便进一步调用product Controller。 现在在测试客户端中打开产品控制器的“allproducts”端点。 测试端点。 我们得到了对所有六种产品的回应: 我将使用这个控制器方法,并使它成为一个OData端点,并在它上面执行几个查询选项。 在方法上面和请求中添加一个名为[Queryable]属性。CreateResponse将productEntities标记为productEntities. asqueryable()。 隐藏,复制Code

[Queryable]
[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts().AsQueryable();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities.AsQueryable());
    throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
}

美元上 现在使用$top查询选项测试API。 在上述端点中,我刚刚添加了“?”$top=2"在服务的端点(像我们附加查询字符串)。这句话的意思是,我只想从服务中获取前两个产品,结果是: 我们只有两种产品。因此,您可以在这里看到,使服务端点可查询非常简单,而且我们不需要编写新服务来实现此结果。让我们尝试更多的选择。 美元的过滤器 您可以使用此选项对记录执行所有筛选。让我们试试$filter查询选项。假设我们需要获取名称为“computer”的所有产品。您可以使用与筛选相同的端点,如下所示。 我使用$filter=ProductName eq 'computer'作为查询字符串,这意味着获取产品名为“computer”的产品。结果,我们只从产品列表中获得一条记录,因为只有一条记录的产品名称是“computer”。 您可以用许多不同的方式使用过滤器,如下所示。 返回所有名称等于“计算机”的产品。 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?$过滤器= ProductName eq“计算机” 退回所有id小于3的产品。 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?过滤器= ProductId lt 3美元 逻辑运算符:返回id >= 3和id <= 5的所有产品。 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?$filter=ProductId ge3和ProductId le 5 字符串函数:返回名称中含有“IPhone”的所有产品。 http://localhost: 50875 / v1 /产品/产品/ allproducts ? $过滤器= substringof(“IPhone”,ProductName) 过滤器选项也可以应用于日期字段。 orderby美元 让我们尝试使用相同的端点进行orderby查询。 返回所有按产品名称降序排序的产品 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby = ProductName desc美元 输出 隐藏,收缩,复制Code

[
   {
      "ProductId":6,
      "ProductName":"Watch"
   },
   {
      "ProductId":8,
      "ProductName":"Titan Watch"
   },
   {
      "ProductId":9,
      "ProductName":"Laptop Bag"
   },
   {
      "ProductId":1,
      "ProductName":"Laptop"
   },
   {
      "ProductId":11,
      "ProductName":"IPhone 6S"
   },
   {
      "ProductId":10,
      "ProductName":"IPhone 6"
   },
   {
      "ProductId":4,
      "ProductName":"IPhone"
   },
   {
      "ProductId":12,
      "ProductName":"HP Laptop"
   },
   {
      "ProductId":2,
      "ProductName":"computer"
   },
   {
      "ProductId":5,
      "ProductName":"Bag"
   }
]

返回所有按产品名称升序排序的产品 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby美元= ProductName asc 输出 隐藏,缩小的;复制Code

[
  {
    "ProductId": 5,
    "ProductName": "Bag"
  },
  {
    "ProductId": 2,
    "ProductName": "computer"
  },
  {
    "ProductId": 12,
    "ProductName": "HP Laptop"
  },
  {
    "ProductId": 4,
    "ProductName": "IPhone"
  },
  {
    "ProductId": 10,
    "ProductName": "IPhone 6"
  },
  {
    "ProductId": 11,
    "ProductName": "IPhone 6S"
  },
  {
    "ProductId": 1,
    "ProductName": "Laptop"
  },
  {
    "ProductId": 9,
    "ProductName": "Laptop Bag"
  },
  {
    "ProductId": 8,
    "ProductName": "Titan Watch"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  }
]

返回所有按产品id降序排序的产品 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby = ProductId desc美元 输出 隐藏,收缩,复制Code

[
  {
    "ProductId": 12,
    "ProductName": "HP Laptop"
  },
  {
    "ProductId": 11,
    "ProductName": "IPhone 6S"
  },
  {
    "ProductId": 10,
    "ProductName": "IPhone 6"
  },
  {
    "ProductId": 9,
    "ProductName": "Laptop Bag"
  },
  {
    "ProductId": 8,
    "ProductName": "Titan Watch"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  },
  {
    "ProductId": 5,
    "ProductName": "Bag"
  },
  {
    "ProductId": 4,
    "ProductName": "IPhone"
  },
  {
    "ProductId": 2,
    "ProductName": "computer"
  },
  {
    "ProductId": 1,
    "ProductName": "Laptop"
  }
]

返回所有按产品id升序排序的产品 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby美元= ProductId asc 输出 隐藏,收缩,复制Code

[
  {
    "ProductId": 1,
    "ProductName": "Laptop"
  },
  {
    "ProductId": 2,
    "ProductName": "computer"
  },
  {
    "ProductId": 4,
    "ProductName": "IPhone"
  },
  {
    "ProductId": 5,
    "ProductName": "Bag"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  },
  {
    "ProductId": 8,
    "ProductName": "Titan Watch"
  },
  {
    "ProductId": 9,
    "ProductName": "Laptop Bag"
  },
  {
    "ProductId": 10,
    "ProductName": "IPhone 6"
  },
  {
    "ProductId": 11,
    "ProductName": "IPhone 6S"
  },
  {
    "ProductId": 12,
    "ProductName": "HP Laptop"
  }
]

orderby美元美元 您可以使用多个查询选项来获取所需的记录。假设我只需要通过ProductId ascending从top order中获取5条记录。为了实现这一点,我可以编写以下查询。 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby美元= ProductId asc& $ = 5 输出 隐藏,复制Code

[
  {
    "ProductId": 1,
    "ProductName": "Laptop"
  },
  {
    "ProductId": 2,
    "ProductName": "computer"
  },
  {
    "ProductId": 4,
    "ProductName": "IPhone"
  },
  {
    "ProductId": 5,
    "ProductName": "Bag"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  }
]

上面的输出获取5条已排序的ProductId记录。 美元跳过 顾名思义,skip查询选项用于跳过记录。让我们考虑以下场景。 选择top 5并跳过3 http://localhost: 50875 / v1 /产品/产品/ allproducts ?顶级= 5,跳过= 3美元 输出 隐藏,复制Code

[
  {
    "ProductId": 5,
    "ProductName": "Bag"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  },
  {
    "ProductId": 8,
    "ProductName": "Titan Watch"
  },
  {
    "ProductId": 9,
    "ProductName": "Laptop Bag"
  },
  {
    "ProductId": 10,
    "ProductName": "IPhone 6"
  }
]

跳过orderby美元美元 按ProductName顺序升序并跳过6 http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby = ProductName asc和跳过美元= 6 输出 隐藏,复制Code

[
  {
    "ProductId": 1,
    "ProductName": "Laptop"
  },
  {
    "ProductId": 9,
    "ProductName": "Laptop Bag"
  },
  {
    "ProductId": 8,
    "ProductName": "Titan Watch"
  },
  {
    "ProductId": 6,
    "ProductName": "Watch"
  }
]

下面是一些标准的过滤操作符和查询函数,您可以使用它们来创建从https://msdn.microsoft.com/en-us/library/gg334767.aspx获取的查询 标准过滤操作符 Web API支持下表中列出的标准OData过滤器操作符。 OperatorDescriptionExampleComparison OperatorseqEqual eq滤波器=收入100000 nenot等于美元过滤器过滤= =收入ne 100000 gtgreater亿美元收入gt 100000 gegreater超过或等于$过滤器=收入通用电气100000 lt ltless过滤器=美元收入100000美元leless超过或等于过滤器=收入勒100000逻辑OperatorsandLogical和过滤器= lt收入100000美元,收入2000 gt orlogical或过滤器=美元包含(名字,“(样本)”)或包含(名称、“测试”)notLogical否定过滤器=美元不包含(名称、“样本”)分组操作符()优先分组(包含(名称,‘sample’)或包含(名称,‘test’))和收入gt 5000 标准查询功能 web API支持这些标准的OData字符串查询函数。 FunctionExamplecontains过滤器=包含美元(名称、(样本))endswith $过滤器= endswith(名字,Inc .) startswith $过滤器= startswith(名字,' ') 分页 您可以创建一个支持分页的端点,这意味着如果数据库中有大量数据,而客户端需要显示每页10条记录之类的数据。因此,建议服务器本身为每个请求发送这10条记录,这样整个数据有效负载就不会在网络上传输。这也可以提高服务的性能。 假设数据库上有10000条记录。您可以使您的端点返回10条记录,并接受对初始记录和要发送的记录数量的请求。在这种情况下,客户端每次都会对下一组记录发出请求,然后使用fetch分页选项,或者用户导航到下一个页面。要启用分页,只需在[Queryable]属性中提到页面计数。例如,[Queryable(PageSize = 10)] 所以我们的方法代码变成: 隐藏,复制Code

[Queryable(PageSize = 10)]
[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts().AsQueryable();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities.AsQueryable());
    throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
}

查询选项约束 您也可以在查询选项上放置约束。假设您不希望客户端访问筛选选项或跳过选项,那么在操作级别您可以设置约束来忽略这种API请求。有四种类型的查询选项约束。 AllowedQueryOptions 例如:[Queryable(AllowedQueryOptions =AllowedQueryOptions)。过滤器| AllowedQueryOptions.OrderBy)] 上面的查询选项示例表明,API上只允许$filter和$orderby查询。 隐藏,复制Code

[Queryable(AllowedQueryOptions =AllowedQueryOptions.Filter | AllowedQueryOptions.OrderBy)]
 [GET("allproducts")]
 [GET("all")]
 public HttpResponseMessage Get()
 {
     var products = _productServices.GetAllProducts().AsQueryable();
     var productEntities = products as List<ProductEntity> ?? products.ToList();
     if (productEntities.Any())
         return Request.CreateResponse(HttpStatusCode.OK, productEntities.AsQueryable());
     throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
 }

当我用$top查询调用端点时。 http://localhost: 50875 / v1 /产品/产品/ allproducts ?顶级= 10美元 我得到了如下回复: 它说, "在URI中指定的查询无效", "ExceptionMessage": "不允许查询选项'Top'。要允许它,在QueryableAttribute或QueryValidationSettings上设置'AllowedQueryOptions'属性。 这意味着它不允许其他类型的queryoptions在这个API端点上工作。 AllowedOrderByProperties 例如:[Queryable(AllowedOrderByProperties = "ProductId")] //提供列/属性列表 这意味着端点只支持基于ProductId的排序。可以指定要启用排序的更多属性。因此,按照以下守则: 隐藏,复制Code

[Queryable(AllowedOrderByProperties = "ProductId")]
[GET("allproducts")]
[GET("all")]
public HttpResponseMessage Get()
{
    var products = _productServices.GetAllProducts().AsQueryable();
    var productEntities = products as List<ProductEntity> ?? products.ToList();
    if (productEntities.Any())
        return Request.CreateResponse(HttpStatusCode.OK, productEntities.AsQueryable());
    throw new ApiDataException(1000, "Products not found", HttpStatusCode.NotFound);
}

如果我尝试调用URL: http://localhost: 50875 / v1 /产品/产品/ allproducts吗?orderby = ProductName desc美元 它给出错误的响应: 说, "在URI中指定的查询无效", "ExceptionMessage": "不允许使用'ProductName'进行订单。为了允许它,在QueryableAttribute或QueryValidationSettings上设置'AllowedOrderByProperties'属性。 URL: http://localhost: 50875 / v1 /产品/产品/ allproducts吗?$orderby=ProductId desc可以正常工作。 AllowedLogicalOperators 示例:[可查询(AllowedLogicalOperators = AllowedLogicalOperators.GreaterThan)] 在上面的例子中,语句声明在查询和查询选项中只允许使用greaterThan(例如,“gt”逻辑运算符)和其他任何“gt”将返回错误的逻辑运算符。您可以在您的应用程序中尝试它。 AllowedArithmeticOperators 示例:[Queryable(AllowedArithmeticOperators = AllowedArithmeticOperators. add)] 在上面的示例中,语句声明在API调用时只允许添加算术运算符。您可以在您的应用程序中尝试它。 结论 OData中有很多东西我无法一蹴而就。目的是给一个想法,我们可以实现什么使用OData。您可以探索更多的选项和属性,并使用REST API。我希望您能够创建一个具有所有必需功能的基本WebAPI应用程序。本系列所有文章附带的代码库可作为创建任何企业级WebAPI应用程序的样板。不断探索,休息。从GitHub下载完整的源代码。 参考文献 http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options https://msdn.microsoft.com/en-us/library/azure/gg312156.aspx 其他系列 我的其他系列文章: MVC: http://www.codeproject.com/Articles/620195/Learning-MVC-Part-Introduction-to-MVC-Architectu OOP: http://www.codeproject.com/Articles/771455/Diving-in-OOP-Day-Polymorphism-and-Inheritance-Ear 本文转载于:http://www.diyabc.com/frontweb/news413.html

原文地址:https://www.cnblogs.com/Dincat/p/13447625.html