翻译自 Camilo Reyes 2020年8月26日的文章 《Build a REST API in .NET Core》
REST API 可以使用简单的动词(如 POST、PUT、PATCH 等)将大型解决方案背后的复杂性隐藏起来。在本文中,Camilo Reyes 解释了如何在 .NET Core 中创建 REST API。
扩展大型复杂解决方案的一种方法是将它们分解为 REST 微服务。微服务开启了 API 背后的业务逻辑的可测试性和可重用性。因为 REST API 可以被多个客户端重用,使得组织可以共享软件模块。客户端或许是移动端、网页端,甚至单页应用中的静态资源端,它们可以调用任意多的 API。
在本文中,我将向您展示在 .NET Core 中构建 REST API 的全过程。我将用现实工作中的需求来解释这个过程,比如版本控制、搜索、日志记录等等。REST 通常与诸如 POST、PUT 或 PATCH 的动词一起使用,因此我打算将它们全部覆盖。我希望您看到的是,使用现有工具交付价值的一个好的有效的方式。
入门介绍本文假定您已掌握了 ASP.NET、C# 和 REST API,因此我不会涉及任何基础知识。我建议在学习本文时使用最新的 .NET Core 版本。如果您想从工作代码开始学习,可以从 GitHub 下载示例代码。
你可以先新建一个文件夹,比如 BuildRestApiNetCore,然后在 shell 中打开它:
dotnet new sln dotnet new webapi --no-https dotnet sln add .该项目基于禁用了 HTTPS 的 Web API 模板,以简化本地开发。双击解决方案文件会在 Visual Studio 中打开它(如果已安装)。为了支持 .NET Core 3.1,请确保安装了 2019 版的 IDE。
由于 API 在客户端和数据库之间建立了一层分离,因此准备数据是一个很好的开始。为了简化数据访问,Entity Framework 提供了一个内存中的替代方案,这样我就可以只关注 API 本身。
通过 NuGet 获取内存数据库提供程序:
dotnet add package Microsoft.EntityFrameworkCore.InMemory然后,创建以下数据模型。我将其放在 Models 文件夹中,以表示此命名空间存放原始数据。若要使用数据标注,请在 using 语句中添加 System.ComponentModel.DataAnnotations。
public class Product { [Key] [Required] [Display(Name = "productNumber")] public string ProductNumber { get; set; } [Required] [Display(Name = "name")] public string Name { get; set; } [Required] [Range(10, 90)] [Display(Name = "price")] public double? Price { get; set; } [Required] [Display(Name = "department")] public string Department { get; set; } }在实际的解决方案中,这可能要根据团队的需要将其放在单独的项目中。请注意分配给该模型的属性,例如 Required、Display 和 Range,这些是 ASP.NET 中的数据标注,用于在模型绑定时验证 Product。因为我使用的是内存数据库,所以 Entity Framework 需要一个唯一的 Key。这些属性指定了验证规则,例如:价格区间或者该属性是否是必须的。
从业务的视角来看,这是一个包含产品编号、名称和价格的电子商务站点。每个产品还指定了一个部门,以便按部门进行搜索。
接下来,在 Models 命名空间中设置 Entity Framework DbContext:
public class ProductContext : DbContext { public ProductContext(DbContextOptions<ProductContext> options) : base(options) { } public DbSet<Product> Products { get; set; } }该数据库上下文被依赖注入到控制器中,用于查询或更新数据。要在 ASP.NET Core 中启用依赖注入,请打开 Startup 类并将其添加到 ConfigureServices 中:
services.AddDbContext<ProductContext>(opt => opt.UseInMemoryDatabase("Products"));这行代码完成了内存数据库。请确保在两个类的 using 语句中添加 Microsoft.EntityFrameworkCore。一个空白的后端是无趣的,因此我们来填充一些种子数据。
创建下面的扩展方法以帮助迭代生成种子数据,可以将它放在 Extensions 命名空间或文件夹中:
public static class EnumerableExtensions { public static IEnumerable<T> Times<T>(this int count, Func<int, T> func) { for (var i = 1; i <= count; i++) yield return func.Invoke(i); } }在 Models 命名空间下添加一个静态类以初始化种子数据:
public static class ProductSeed { public static void InitData(ProductContext context) { var rnd = new Random(); var adjectives = new[] { "Small", "Ergonomic", "Rustic", "Smart", "Sleek" }; var materials = new[] { "Steel", "Wooden", "Concrete", "Plastic", "Granite", "Rubber" }; var names = new[] { "Chair", "Car", "Computer", "Pants", "Shoes" }; var departments = new[] { "Books", "Movies", "Music", "Games", "Electronics" }; context.Products.AddRange(900.Times(x => { var adjective = adjectives[rnd.Next(0, 5)]; var material = materials[rnd.Next(0, 5)]; var name = names[rnd.Next(0, 5)]; var department = departments[rnd.Next(0, 5)]; var productId = $"{x,-3:000}"; return new Product { ProductNumber = $"{department.First()}{name.First()}{productId}", Name = $"{adjective} {material} {name}", Price = (double)rnd.Next(1000, 9000) / 100, Department = department }; })); context.SaveChanges(); } }