这段代码循环遍历一个 900 条数据的列表以生成大量的产品,这些产品的部门、价格和名称都是随机捡选的。每个产品都有一个“巧妙”的 key 作为主键,该主键由部门、名称和产品 Id 组合而成。
有了这些种子数据,您就可以得到诸如在 Electronics 部门带有标价的名为 “Smart Wooden Pants” 的产品了。
作为开始构建 Endpoints的第一步,设置 API 版本是一个好主意。这使得客户端应用可以随时升级 API 功能,而无需紧密耦合。
API 版本控制来自一个 NuGet 包:
dotnet add package Microsoft.AspNetCore.Mvc.Versioning回到 Startup 类,并将其添加到 ConfigureServices 中:
services.AddApiVersioning(opt => opt.ReportApiVersions = true);我选择在 API 响应中包含可用的版本号,以便客户端知道何时有升级可用。我推荐使用 语义化的版本控制 来传达 API 中的重大更改。让客户端知道每次升级都修改了什么,这样有助于每个客户端保持最新的功能。
REST API 中的搜索 Endpoint要构建一个 Endpoint,请在 Controllers 文件夹中转到 ASP.NET 中的 Controller。
使用下面的代码创建一个 ProductsController,请确保在 using 语句中添加 Microsoft.AspNetCore.Mvc 命名空间:
[ApiController] [ApiVersion("1.0")] [Route("v{version:apiVersion}/[controller]")] [Produces("application/json")] public class ProductsController : ControllerBase { private readonly ProductContext _context; public ProductsController(ProductContext context) { _context = context; if (_context.Products.Any()) return; ProductSeed.InitData(context); } }请注意,当数据库中没有任何产品时,将运行 InitData 初始化种子数据。我设置了一个带有版本控制的 Route,版本号通过 ApiVersion 设置。通过依赖注入将数据上下文 ProductContext 注入到构造函数中。在该 Controller 中,第一个 Endpoint 是返回一个产品列表的 GET:
[HttpGet] [Route("")] [ProducesResponseType(StatusCodes.Status200OK)] public ActionResult<IQueryable<Product>> GetProducts() { var result = _context.Products as IQueryable<Product>; return Ok(result.OrderBy(p => p.ProductNumber)); }请确保在 using 语句中添加 Microsoft.AspNetCore.Http,以设置响应类型中的状态码。
我选择按照产品编号排序产品,以便更简单地显示结果。在生产系统中,可以检查这种排序是否与聚集索引相匹配,以便减轻数据库的运行压力。经常检查执行计划和统计 IO,以确认有良好的性能。
此项目已经可以进行测试了!在命令行中运行以下命令:
dotnet watch run使用 curl 测试该 Endpoint:
curl -i -X GET "http://localhost:5000/v1/products" -H "accept: application/json"我在两个独立的控制台窗口中运行上面这两条命令。一个以监视模式运行项目,当我更改代码文件时,会自动重新生成并刷新;另一个是我保持 curl 结果的地方。您可以使用 Postman,但是伴随 Windows 10 而来的 curl 也可以完成该工作。
结果如下:
该请求返回数据库中的所有产品,但它不可扩展。随着产品列表的增加,客户端将受到未过滤数据的猛烈冲击,从而给 SQL 和网络流量带来更大的压力。
更好的方法是在一个模型中引入 limit 和 offset 请求参数:
public class ProductRequest { [FromQuery(Name = "limit")] public int Limit { get; set; } = 15; [FromQuery(Name = "offset")] public int Offset { get; set; } }将此请求参数关联到 GetProducts Endpoint:
public ActionResult<IQueryable<Product>> GetProducts([FromQuery] ProductRequest request) { var result = _context.Products as IQueryable<Product>; Response.Headers["x-total-count"] = result.Count().ToString(); return Ok(result .OrderBy(p => p.ProductNumber) .Skip(request.Offset) .Take(request.Limit)); }请注意我设置了一个值为 Count 的 HTTP header x-total-count,用于帮助想要分页浏览整个结果集的客户端。如果未指定请求参数,则该 API 默认返回前 15 条数据。
接下来,添加一个搜索参数,按部门筛选产品:
public ActionResult<IQueryable<Product>> GetProducts([FromQuery] string department, [FromQuery] ProductRequest request) { // ... if (!string.IsNullOrEmpty(department)) { result = result.Where(p => p.Department.StartsWith(department, StringComparison.InvariantCultureIgnoreCase)); } // .. }