目录
介绍
创建测试RESTful WEB API服务
应用程序架构
数据库
创建ASP.NET核心WEB API应用程序
使用实体框架核心进行数据库访问
异步设计模式
存储库
存储库实现
服务
服务接口
服务的实现
产品和价格表之间的数据完整性
服务模式
控制器
创建控制器
检查应用程序
使用ASP.NET Core 2.1创建测试RESTful WEB API服务
- 从GitHub下载源代码
- 第二部分
在本文中,我们将介绍使用ASP.NET Core创建ASP.NET WEB API应用程序的过程。主要重点是应用程序的生产力。
创建测试RESTful WEB API服务在这篇文章中,我们将创建一个异步RESTful WEB API服务,该服务能够搜索数据库中的产品并获取特定产品的不同供应商的价目表。
对于编码,我们需要Microsoft Visual Studio 2017(更新到.NET Core 2.1)和Microsoft SQL Server(任何版本)。
应用程序架构我们将使用控制器——服务——存储库——数据库架构来构建我们的WEB API应用程序。
控制器负责路由——它们接受http请求并使用从请求参数或主体接收的参数调用适当的服务方法。按照惯例,我们将业务逻辑封装为“服务”的类命名。处理完请求后,服务会将IActionResult类型的结果返回给控制器。控制器不关心服务结果的类型,只是通过http响应将其传输给用户。从数据库接收数据或将数据存储在数据库中的所有方法都封装在存储库中。如果服务需要一些数据,它会在不知道数据存储位置和方式的情况下请求存储库。
此模式提供了应用程序层的最大分离,并使开发和测试应用程序变得容易。
数据库在我们的应用程序中,我们使用Microsoft SQL Server。让我们为我们的应用程序创建一个数据库,并用测试数据填充它,并在Microsoft SQL Server Management Studio中执行下一个查询:
USE [master]
GO
CREATE DATABASE [SpeedUpCoreAPIExampleDB]
GO
USE [SpeedUpCoreAPIExampleDB]
GO
CREATE TABLE [dbo].[Products] (
[ProductId] INT IDENTITY (1, 1) NOT NULL,
[SKU] NCHAR (50) NOT NULL,
[Name] NCHAR (150) NOT NULL,
CONSTRAINT [PK_Products] PRIMARY KEY CLUSTERED ([ProductId] ASC)
);
GO
CREATE TABLE [dbo].[Prices] (
[PriceId] INT IDENTITY (1, 1) NOT NULL,
[ProductId] INT NOT NULL,
[Value] DECIMAL (18, 2) NOT NULL,
[Supplier] NCHAR (50) NOT NULL,
CONSTRAINT [PK_Prices] PRIMARY KEY CLUSTERED ([PriceId] ASC)
);
GO
ALTER TABLE [dbo].[Prices] WITH CHECK ADD CONSTRAINT [FK_Prices_Products] FOREIGN KEY([ProductId])
REFERENCES [dbo].[Products] ([ProductId])
ON DELETE CASCADE
GO
ALTER TABLE [dbo].[Prices] CHECK CONSTRAINT [FK_Prices_Products]
GO
INSERT INTO Products ([SKU], [Name]) VALUES ('aaa', 'Product1');
INSERT INTO Products ([SKU], [Name]) VALUES ('aab', 'Product2');
INSERT INTO Products ([SKU], [Name]) VALUES ('abc', 'Product3');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (1, 100, 'Bosch');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (1, 125, 'LG');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (1, 130, 'Garmin');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (2, 140, 'Bosch');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (2, 145, 'LG');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (2, 150, 'Garmin');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (3, 160, 'Bosch');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (3, 165, 'LG');
INSERT INTO Prices ([ProductId], [Value], [Supplier]) VALUES (3, 170, 'Garmin');
GO
现在我们有一个名称为SpeedUpCoreAPIExampleDB的数据库,并填充了测试数据。
该Products表包含一个products列表。SKU字段用于products在列表中搜索。
该Prices表包含价格清单。
这些表之间的关系可以用下图表示:
注意!我们使用CASCADE删除规则创建了一个名为FK_Prices_Products的FOREIGN KEY ,以便MS SQL服务器能够在Products表中删除记录时提供Products和Prices表之间的数据完整性。
创建ASP.NET核心WEB API应用程序在Microsoft Visual Studio中,启动新的.NET Core项目SpeedUpCoreAPIExample。
然后选择Web API:
由于我们正在创建 Web API Сore应用程序,我们应该安装Microsoft.AspNetCore.Mvc的NuGet包。转到菜单 主菜单>工具> NuGet包管理器>管理器NuGet包 用于解决方案和输入Microsoft.AspNetCore.Mvc。选择并安装包:
下一步是创建我们的应用程序的数据模型。由于我们已经创建了数据库,因此使用脚手架机制从数据库结构生成数据模型似乎是合乎逻辑的。但是我们不会使用脚手架,因为数据库结构不会完全反映应用程序数据模型——在数据库中,我们遵循命名表的惯例,如“ Products”和“ Prices” 这样的复数名称,考虑到表分别是 “ Product”和“ Price” 行的集合。在我们的应用程序中,我们想要命名实体类“ Product”和“ Price”,但在使用脚手架之后,我们将创建名称为“ Products”和“ Prices”的实体,并且还会自动创建反映实体之间关系的一些其他对象。
因此,我们必须重写代码。这就是为什么我们决定手动创建数据模型。
在Solution Explorer中,右键单击您的项目,然后选择Add> New Folder。
将其命名为Models。在Models文件夹中,让我们创建两个实体类,Product.cs和Price.cs。
右键单击Models文件夹,然后选择Add Item> Class> Product.cs。
输入Product类内容:
namespace SpeedUpCoreAPIExample.Models
{
public class Product
{
public int ProductId { get; set; }
public string Sku { get; set; }
public string Name { get; set; }
}
}
而后是Price.cs类的内容:
namespace SpeedUpCoreAPIExample.Models
{
public class Price
{
public int PriceId { get; set; }
public int ProductId { get; set; }
public decimal Value { get; set; }
public string Supplier { get; set; }
}
}
在Price类中,我们使用该Value字段来存储价格值——我们不能将字段命名为“ Price”,因为字段不能与类的名称相同(我们还在数据库的Prices表中使用“ Value”字段)。稍后在Database上下文类中,我们将这些实体映射到数据库表“ Products”和“ Prices”。
请注意,在模型中,我们在产品和价格实体之间没有实现任何关系。
使用实体框架核心进行数据库访问要访问数据库,我们将使用Entity Framework Core。为此,我们需要为我们的数据库安装一个提供程序EntityFrameworkCore。转到菜单主菜单>工具> NuGet包管理器>管理器NuGet包 用于解决方案并在“浏览”字段中输入Microsoft.EntityFrameworkCore.SqlServer,因为我们使用的是Microsoft SQL Server。选择并安装包:
为了告知实体框架如何使用我们的数据模型,我们应该创建一个Database上下文类。为此,让我们创建一个新文件夹Contexts,右键单击它并选择Add> New Item> ASP.NET Core> Code> Class。将类命名为DefaultContext.cs。
输入该类的以下内容:
using Microsoft.EntityFrameworkCore;
using SpeedUpCoreAPIExample.Models;
namespace SpeedUpCoreAPIExample.Contexts
{
public class DefaultContext : DbContext
{
public virtual DbSet Products { get; set; }
public virtual DbSet Prices { get; set; }
public DefaultContext(DbContextOptions options) : base(options)
{
}
}
}
在以下几行中,我们将数据模型实体类映射到数据库表:
public virtual DbSet Products { get; set; }
public virtual DbSet Prices { get; set; }
根据实体键名称的Entity Framework Core 命名约定,模型键字段应具有名称“ Id”或EntitynameId(不区分大小写)以由EFC自动映射到数据库键。我们使用符合惯例的“ ProductId”和“ PriceId”名称。如果我们对关键字段使用非标准名称,我们必须显示的在DbContext中配置关键字。
数据库上下文的下一步是在我们的应用程序的Startup类中声明它。打开应用程序根目录中的Startup.cs文件,在ConfigureServices方法中添加“ using”指令:
using Microsoft.EntityFrameworkCore;
using SpeedUpCoreAPIExample.Contexts;
并修正ConfigureServices程序。
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultDatabase")));
}
最后一步是配置数据库的连接字符串。为此,在我们的应用程序的根目录中找到appsettings.json文件并添加以下ConnectionStrings会话:
"ConnectionStrings": {
"DefaultDatabase": "Server=localhost;Database=SpeedUpCoreAPIExampleDB;Integrated Security=True;"
}
但要注意,在应用程序的根目录中,还有一个appsettings.Development.json配置文件。默认情况下,在开发过程中使用此文件。所以,你应该在那里复制ConnectionStrings会话否则Configuration.GetConnectionString将返回null。
现在,我们的应用程序已准备好使用数据库。
异步设计模式异步工作是提高应用程序生产力的第一步。异步的所有好处将在第2部分中讨论。
我们希望所有存储库都异步工作,因此它们将返回Task并且所有方法都将具有带Async后缀的名称。后缀不会使方法异步。按惯例,它用于表示我们对该方法的意图。组合async- await实现异步模式。
存储库我们将创建两个存储库——一个用于Product实体,另一个用于Price实体。我们将首先在适当的接口中声明存储库方法,以使存储库为依赖注入做好准备。
让我们为接口创建一个新的文件夹接口。右键单击Interfaces文件夹并添加一个名为IProductsRepository.cs的新类,并更改其代码:
using SpeedUpCoreAPIExample.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Repositories
{
public interface IProductsRepository
{
Task GetAllProductsAsync();
Task GetProductAsync(int productId);
Task FindProductsAsync(string sku);
Task DeleteProductAsync(int productId);
}
}
然后是IPricesRepository.cs的代码:
using SpeedUpCoreAPIExample.Models;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Interfaces
{
public interface IPricesRepository
{
Task GetPricesAsync(int productId);
}
}
存储库实现
创建一个新文件夹Repositories并使用以下代码添加类ProductsRepository:
using Microsoft.EntityFrameworkCore;
using SpeedUpCoreAPIExample.Contexts;
using SpeedUpCoreAPIExample.Interfaces;
using SpeedUpCoreAPIExample.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Repositories
{
public class ProductsRepository : IProductsRepository
{
private readonly DefaultContext _context;
public ProductsRepository(DefaultContext context)
{
_context = context;
}
public async Task GetAllProductsAsync()
{
return await _context.Products.ToListAsync();
}
public async Task GetProductAsync(int productId)
{
return await _context.Products.Where(p => p.ProductId == productId).FirstOrDefaultAsync();
}
public async Task FindProductsAsync(string sku)
{
return await _context.Products.Where(p => p.Sku.Contains(sku)).ToListAsync();
}
public async Task DeleteProductAsync(int productId)
{
Product product = await GetProductAsync(productId);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
return product;
}
}
}
在ProductsRepository类构造函数中,我们使用依赖注入注入DefaultContext。
然后用下面代码创建PricesRepository.cs:
using Microsoft.EntityFrameworkCore;
using SpeedUpCoreAPIExample.Contexts;
using SpeedUpCoreAPIExample.Interfaces;
using SpeedUpCoreAPIExample.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Repositories
{
public class PricesRepository : IPricesRepository
{
private readonly DefaultContext _context;
public PricesRepository(DefaultContext context)
{
_context = context;
}
public async Task GetPricesAsync(int productId)
{
return await _context.Prices.Where(p => p.ProductId == productId).ToListAsync();
}
}
}
最后一步是在Startup类中声明我们的存储库。在Startup.cs的ConfigureServices方法中添加“using”指令:
using SpeedUpCoreAPIExample.Interfaces;
using SpeedUpCoreAPIExample.Repositories;
并在DefaultContext后声明:
services.AddScoped();
services.AddScoped();
注意!声明序列对依赖注入很重要——如果要注入DefaultContext到存储库,DefaultContext则应在存储库之前声明。对于存储库,我们使用Scoped生命周期模型,因为系统自动向Scoped模型注册数据库上下文。使用上下文的存储库应该具有相同的生命周期模型。
服务我们的“ Services”类封装了除访问数据库之外的所有业务逻辑——为此,它们注入了存储库。所有服务方法都异步运行并根据数据处理结果返回IActionResult。服务也处理错误并相应地执行输出结果格式化。
在我们开始实施服务之前,让我们考虑一下我们要发送给用户的数据。例如,我们的模型类“ Price”有两个字段“ PriceId”和“ ProductId”用于从数据库中获取数据,但它们对用户没有任何意义。更重要的是,如果我们的API响应整个实体,我们偶尔会发现一些敏感数据。除此之外,我们将使用“ Price”字段来返回在使用价格表时更常见的价格值。我们将使用“ Id”字段返回ProductId。“ Id”名称将对应于API产品标识参数的名称(将在“控制器”部分中显示)。
因此,使用一组有限的字段为输出创建数据模型是一种很好的做法。
让我们创建一个新文件夹ViewModels并在那里添加两个类:
namespace SpeedUpCoreAPIExample.ViewModels
{
public class ProductViewModel
{
public int Id { get; set; }
public string Sku { get; set; }
public string Name { get; set; }
}
}
和:
namespace SpeedUpCoreAPIExample.ViewModels
{
public class PriceViewModel
{
public decimal Price { get; set; }
public string Supplier { get; set; }
}
}
这些类是实体类的缩短、安全版本,Product并Price没有额外的字段和调整后的字段名。
服务接口一项服务可以完成所有工作,但我们将创建与存储库一样多个服务。像往常一样,我们从在接口中声明服务方法开始。
右键单击Interfaces文件夹并使用以下代码创建一个新IProductsService类:
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Interfaces
{
public interface IProductsService
{
Task GetAllProductsAsync();
Task GetProductAsync(int productId);
Task FindProductsAsync(string sku);
Task DeleteProductAsync(int productId);
}
}
然后,IPricesService中的代码:
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Interfaces
{
public interface IPricesService
{
Task GetPricesAsync(int productId);
}
}
服务的实现
创建一个新文件夹,Services,并添加一个新类ProductsService:
using Microsoft.AspNetCore.Mvc;
using SpeedUpCoreAPIExample.Interfaces;
using SpeedUpCoreAPIExample.Models;
using SpeedUpCoreAPIExample.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Services
{
public class ProductsService : IProductsService
{
private readonly IProductsRepository _productsRepository;
public ProductsService(IProductsRepository productsRepository)
{
_productsRepository = productsRepository;
}
public async Task FindProductsAsync(string sku)
{
try
{
IEnumerable products = await _productsRepository.FindProductsAsync(sku);
if (products != null)
{
return new OkObjectResult(products.Select(p => new ProductViewModel()
{
Id = p.ProductId,
Sku = p.Sku.Trim(),
Name = p.Name.Trim()
}
));
}
else
{
return new NotFoundResult();
}
}
catch
{
return new ConflictResult();
}
}
public async Task GetAllProductsAsync()
{
try
{
IEnumerable products = await _productsRepository.GetAllProductsAsync();
if (products != null)
{
return new OkObjectResult(products.Select(p => new ProductViewModel()
{
Id = p.ProductId,
Sku = p.Sku.Trim(),
Name = p.Name.Trim()
}
));
}
else
{
return new NotFoundResult();
}
}
catch
{
return new ConflictResult();
}
}
public async Task GetProductAsync(int productId)
{
try
{
Product product = await _productsRepository.GetProductAsync(productId);
if (product != null)
{
return new OkObjectResult(new ProductViewModel()
{
Id = product.ProductId,
Sku = product.Sku.Trim(),
Name = product.Name.Trim()
});
}
else
{
return new NotFoundResult();
}
}
catch
{
return new ConflictResult();
}
}
public async Task DeleteProductAsync(int productId)
{
try
{
Product product = await _productsRepository.DeleteProductAsync(productId);
if (product != null)
{
return new OkObjectResult(new ProductViewModel()
{
Id = product.ProductId,
Sku = product.Sku.Trim(),
Name = product.Name.Trim()
});
}
else
{
return new NotFoundResult();
}
}
catch
{
return new ConflictResult();
}
}
}
}
而PricesService类如下:
using Microsoft.AspNetCore.Mvc;
using SpeedUpCoreAPIExample.Interfaces;
using SpeedUpCoreAPIExample.Models;
using SpeedUpCoreAPIExample.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Services
{
public class PricesService : IPricesService
{
private readonly IPricesRepository _pricesRepository;
public PricesService(IPricesRepository pricesRepository)
{
_pricesRepository = pricesRepository;
}
public async Task GetPricesAsync(int productId)
{
try
{
IEnumerable pricess = await _pricesRepository.GetPricesAsync(productId);
if (pricess != null)
{
return new OkObjectResult(pricess.Select(p => new PriceViewModel()
{
Price = p.Value,
Supplier = p.Supplier.Trim()
}
)
.OrderBy(p => p.Price)
.ThenBy(p => p.Supplier)
);
}
else
{
return new NotFoundResult();
}
}
catch
{
return new ConflictResult();
}
}
}
}
产品和价格表之间的数据完整性
在ProductsService中,我们有DeleteProductAsync方法,它为PricesRepository调用适当的方法从Products表中删除产品数据行。我们还通过外键FK_Prices_Products在Products和Prices表之间建立了关系。由于FK_Prices_Products外键具有CASCADE删除规则,因此从Products表中删除记录时,Prices表中的相关记录也将自动删除。
还有一些其他可能的方法来强制数据完整性,而无需在数据库中使用级联删除外键。例如,我们可以配置Entity Framework以使用该WillCascadeOnDelete()方法执行级联删除。但这也需要重新设计我们的数据模型。另一种方法是在PricesService中实现一个方法DeletePricessAsync,并使用DeleteProductAsync的方法调用它。但是我们必须考虑在单个事务中执行此操作,因为我们的应用程序在Product已经删除但是价格没有删除时可能会失败。因此,我们可能会失去数据完整性。
在我们的示例中,我们使用带有级联删除的外键来强制数据完整性。
注意!显然的,在实际应用中,DeleteProductAsync不应该如此容易地调用该方法,因为重要数据可能是偶然或故意丢失的。在我们的示例中,我们仅使用它来公开数据完整性概念。
服务模式在服务的构造函数中,我们通过依赖注入注入适当的存储库。每个方法从存储库内部的try- catch构造中获取数据并根据数据处理结果返回IActionResult。返回dataset时,数据将从ViewModel文件夹中转换为类。
注意,该响应OkObjectResult(),NotFoundResult(),ConflictResult()等分别对应Controller的ControllerBase Ok(),NotFound(),Conflict()方法。Service将其响应发送给Controller具有相同IActionResult类型的响应,作为Controller发送给用户的响应。这意味着Controller可以直接将响应传递给用户,而无需对其进行校准。
服务的最后一步是在Startup类上声明它们。添加using指令:
using SpeedUpCoreAPIExample.Services;
并在存储库声明后在ConfigureServices方法中声明Services:
services.AddTransient();
services.AddTransient();
由于我们的服务是轻量级和无状态的,因此我们可以使用Transient Services范围模型。
这个阶段的最终ConfigureServices方法是:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddDbContext(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultDatabase")));
services.AddScoped();
services.AddScoped();
services.AddTransient();
services.AddTransient();
}
控制器
在我们的设计模式中,我们没有为控制器留下太多东西,只是作为传入请求的网关——没有业务逻辑,数据访问,错误处理等等。控制器将根据其路由接收传入请求,调用通过依赖注入注入的服务的适当方法,并返回这些方法的结果。
像往常一样,我们将创建两个小型控制器而不是一个大型控制器,因为它们使用逻辑上不同的数据并且具有与其API不同的路径:
ProductsController路由:
[HttpGet]路由
- /api/products——返回整个产品列表
- /api /products/1——返回Id = 1的一个产品
- /api/products/find/aaa——返回Sku字段由参数“sku”值“aaa”组成的产品列表
[HttpDelete]路由
- /api/product/1——删除Id = 1的产品及其价格(级联)
PricesControlle路由:
[HttpGet]路由
- /api/prices/1——返回Id = 1的产品价格清单
右键单击Controllers文件夹,然后选择Add Item> Class> ProductsController.cs并更改为以下内容:
using Microsoft.AspNetCore.Mvc;
using SpeedUpCoreAPIExample.Interfaces;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class ProductsController : Controller
{
private readonly IProductsService _productsService;
public ProductsController(IProductsService productsService)
{
_productsService = productsService;
}
// GET /api/products
[HttpGet]
public async Task GetAllProductsAsync()
{
return await _productsService.GetAllProductsAsync();
}
// GET /api/products/5
[HttpGet("{id}")]
public async Task GetProductAsync(int id)
{
return await _productsService.GetProductAsync(id);
}
// GET /api/products/find
[HttpGet("find/{sku}")]
public async Task FindProductsAsync(string sku)
{
return await _productsService.FindProductsAsync(sku);
}
// DELETE /api/products/5
[HttpDelete("{id}")]
public async Task DeleteProductAsync(int id)
{
return await _productsService.DeleteProductAsync(id);
}
}
}
Controller的名字应该有“ Controller”后缀。使用该指令[Route("api/[controller]")]意味着所有ProductsController的基本路由,控制器的API将是 /api/products。
同样的方式处理PricesController控制器:
using Microsoft.AspNetCore.Mvc;
using SpeedUpCoreAPIExample.Interfaces;
using System.Threading.Tasks;
namespace SpeedUpCoreAPIExample.Contexts
{
[Route("api/[controller]")]
[ApiController]
public class PricesController : ControllerBase
{
private readonly IPricesService _pricesService;
public PricesController(IPricesService pricesService)
{
_pricesService = pricesService;
}
// GET /api/prices/1
[HttpGet("{Id}")]
public async Task GetPricesAsync(int id)
{
return await _pricesService.GetPricesAsync(id);
}
}
}
现在第一次启动我们的应用程序的一切都准备好了。在我们启动应用程序之前,让我们查看应用程序的文件夹/ Properties中的launchSettings.json文件。我们可以看到,“launchUrl”:“api/values”。让我们删除“ / values ”。在这个文件中,我们也可以在applicationUrl参数:applicationUrl": "http://localhost:49858/中更改端口号,在我们的例子中,端口是49858。
我们可以从Controllers文件夹中删除ValuesController.cs控制器。控制器是由Visual Studio自动创建的,我们不在我们的应用程序中使用它。
单击主菜单>调试>启动不调试(或按Ctrl + F5)启动应用程序。该应用程序将在Internet Explorer浏览器中打开(默认情况下),URL将为http://localhost:49858/api。
检查应用程序我们将使用Swagger工具来检查我们的应用程序。最好使用谷歌浏览器或Firefox浏览器。因此,打开Firefox并在URL字段中输入https://inspector.swagger.io/builder。系统将要求您安装Swagger Inspector Extension。
添加扩展名。
现在我们在浏览器中有一个按钮来启动扩展。打开它,选择GET的方法和输入API的URL:
http://localhost:49858/api/products
单击发送按钮,您将收到所有产品的json格式列表:
检查具体Product:
http://localhost:49858/api/products/1
按SKU部分查找产品:
http://localhost:49858/api/products/find/aa
然后检查PricesControllerAPI:
http://localhost:49858/api/prices/1
检查DeleteAPI。
在Swagger中更改方法DELETE并调用API:
http://localhost:49858/api/products/3
要检查缺失的结果,我们可以使用GET方法调用API 的http://localhost:49858/api/products/3 。结果将是404 Not Found。
调用http://localhost:49858/api/prices/3 将返回一组空的价格集合。
原文地址:https://www.codeproject.com/Articles/1260600/Speed-Up-ASP-NET-Core-WEB-API-Application-Part-1