第六章:Web API 开发 第六章:ASP.NET Web API 开发 6.1 Web API 简介与优势 Web API (Application Programming Interface) 是一种构建 HTTP 服务的框架,允许不同应用程序之间通过网络进行通信。与传统的 Web 应用程序不同,Web API 主要提供数据和服务,而不是用户界面。 优势: 平台无关性: 任何能够发送 HTTP 请求并解析响应的客户端都可以使用 Web API。 可扩展性: 易于扩展以满足不断增长的需求。 灵活性: 可以返回各种格式的数据,如 JSON、XML 等。 易于集成: 方便与其他系统和服务集成。
Web API (Application Programming Interface) 是一种构建 HTTP 服务的框架,允许不同应用程序之间通过网络进行通信。与传统的 Web 应用程序不同,Web API 主要提供数据和服务,而不是用户界面。
优势:
平台无关性: 任何能够发送 HTTP 请求并解析响应的客户端都可以使用 Web API。
可扩展性: 易于扩展以满足不断增长的需求。
灵活性: 可以返回各种格式的数据,如 JSON、XML 等。
易于集成: 方便与其他系统和服务集成。
RESTful 支持: 鼓励使用 REST (Representational State Transfer) 架构风格。
6.2.1 创建 ASP.NET Core Web API 项目
在 Visual Studio 中,选择 "创建新项目"。
选择 "ASP.NET Core Web API"。
配置项目名称、位置等,然后点击 "创建"。
选择 ".NET 8.0 (长期支持)" 或者更新的版本,可以选择是否启用OpenAPI支持,然后点击 "创建"。
6.2.2 默认代码分析
创建项目后,你会看到一个默认的 WeatherForecastController.cs 文件。这就是一个简单的 Web API 控制器。
using Microsoft.AspNetCore.Mvc; namespace WebApiDemo.Controllers; [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; private readonly ILogger<WeatherForecastController> _logger; public WeatherForecastController(ILogger<WeatherForecastController> logger) { _logger = logger; } [HttpGet(Name = "GetWeatherForecast")] public IEnumerable<WeatherForecast> Get() { return Enumerable.Range(1, 5).Select(index => new WeatherForecast { Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)), TemperatureC = Random.Shared.Next(-20, 55), Summary = Summaries[Random.Shared.Next(Summaries.Length)] }) .ToArray(); } }
代码解释:
[ApiController]:特性,标记该类为 API 控制器。
[Route("[controller]")]:特性,定义路由模板。[controller] 会被替换为控制器名称(不含 "Controller" 后缀)。例如,对于 WeatherForecastController,路由将是 /WeatherForecast。
ControllerBase:所有 API 控制器的基类。
[HttpGet(Name = "GetWeatherForecast")]:特性,指定 Get() 方法处理 HTTP GET 请求。 Name属性用于命名路由,方便以后引用。
IEnumerable<WeatherForecast>:指定方法返回一个 WeatherForecast 对象的集合。
6.2.3 运行和测试
运行项目后,可以通过浏览器或工具(如 Postman、Swagger UI)访问 /WeatherForecast 路由,查看返回的 JSON 数据。 默认情况下,如果启用了OpenAPI支持,可以通过/swagger访问Swagger UI界面。
6.3.1 控制器
控制器负责处理传入的 HTTP 请求,并返回响应。 每个控制器通常对应于应用程序中的一个特定资源或功能领域。
6.3.2 路由
路由决定了如何将 HTTP 请求映射到特定的控制器和动作。ASP.NET Web API 支持多种路由方式:
基于约定的路由: 使用预定义的路由模板,例如 api/{controller}/{id}。
基于属性的路由: 使用特性(如 [Route], [HttpGet], [HttpPost] 等)在控制器和动作上直接指定路由。
6.3.3 动作
动作是控制器中的方法,负责处理特定的 HTTP 请求。 动作可以使用不同的 HTTP 方法特性(如 [HttpGet], [HttpPost], [HttpPut], [HttpDelete])来区分不同类型的请求。
示例:
[ApiController] [Route("api/[controller]")] public class ProductsController : ControllerBase { private static List<Product> _products = new List<Product>() { new Product { Id = 1, Name = "Product 1", Price = 10.00M }, new Product { Id = 2, Name = "Product 2", Price = 20.00M } }; [HttpGet] public ActionResult<IEnumerable<Product>> GetProducts() { return _products; } [HttpGet("{id}")] public ActionResult<Product> GetProduct(int id) { var product = _products.FirstOrDefault(p => p.Id == id); if (product == null) { return NotFound(); } return product; } [HttpPost] public ActionResult<Product> CreateProduct(Product product) { product.Id = _products.Max(p => p.Id) + 1; _products.Add(product); return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product); } [HttpPut("{id}")] public IActionResult UpdateProduct(int id, Product product) { if (id != product.Id) { return BadRequest(); } var existingProduct = _products.FirstOrDefault(p => p.Id == id); if (existingProduct == null) { return NotFound(); } existingProduct.Name = product.Name; existingProduct.Price = product.Price; return NoContent(); } [HttpDelete("{id}")] public IActionResult DeleteProduct(int id) { var product = _products.FirstOrDefault(p => p.Id == id); if (product == null) { return NotFound(); } _products.Remove(product); return NoContent(); } } public class Product { public int Id { get; set; } public string Name { get; set; } public decimal Price { get; set; } }
代码解释:
[Route("api/[controller]")]:定义了路由前缀 api/Products。
GetProducts():处理 GET 请求,返回所有产品。
GetProduct(int id):处理 GET 请求,返回指定 ID 的产品。
CreateProduct(Product product):处理 POST 请求,创建新产品。
UpdateProduct(int id, Product product):处理 PUT 请求,更新指定 ID 的产品。
DeleteProduct(int id):处理 DELETE 请求,删除指定 ID 的产品。
ActionResult<T>: 允许action返回具体的类型或ActionResult,例如NotFound()或BadRequest()。
CreatedAtAction: 返回201 Created 响应,并包含新创建资源的 URI。
Web API 通常使用 JSON 或 XML 格式来传输数据。ASP.NET Web API 自动处理数据的序列化和反序列化。
6.4.1 JSON
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式,易于阅读和编写。ASP.NET Web API 默认使用 JSON.NET 库进行 JSON 序列化和反序列化。
6.4.2 XML
XML (Extensible Markup Language) 是一种标记语言,用于描述数据结构。ASP.NET Web API 也支持 XML 序列化和反序列化。
示例:
当客户端发送一个包含 JSON 数据的 POST 请求到 CreateProduct 动作时,ASP.NET Web API 会自动将 JSON 数据反序列化为 Product 对象。
依赖注入 (DI) 是一种设计模式,用于降低组件之间的耦合度。ASP.NET Core 内置了 DI 容器,可以方便地将服务注入到控制器中。
示例:
public interface IProductRepository { IEnumerable<Product> GetProducts(); Product GetProduct(int id); void AddProduct(Product product); void UpdateProduct(Product product); void DeleteProduct(int id); } public class ProductRepository : IProductRepository { private static List<Product> _products = new List<Product>() { new Product { Id = 1, Name = "Product 1", Price = 10.00M }, new Product { Id = 2, Name = "Product 2", Price = 20.00M } }; public IEnumerable<Product> GetProducts() { return _products; } public Product GetProduct(int id) { return _products.FirstOrDefault(p => p.Id == id); } public void AddProduct(Product product) { product.Id = _products.Max(p => p.Id) + 1; _products.Add(product); } public void UpdateProduct(Product product) { var existingProduct = _products.FirstOrDefault(p => p.Id == product.Id); if (existingProduct != null) { existingProduct.Name = product.Name; existingProduct.Price = product.Price; } } public void DeleteProduct(int id) { var product = _products.FirstOrDefault(p => p.Id == id); if (product != null) { _products.Remove(product); } } } [ApiController] [Route("api/[controller]")] public class ProductsController : ControllerBase { private readonly IProductRepository _productRepository; public ProductsController(IProductRepository productRepository) { _productRepository = productRepository; } [HttpGet] public ActionResult<IEnumerable<Product>> GetProducts() { return _productRepository.GetProducts().ToList(); } [HttpGet("{id}")] public ActionResult<Product> GetProduct(int id) { var product = _productRepository.GetProduct(id); if (product == null) { return NotFound(); } return product; } [HttpPost] public ActionResult<Product> CreateProduct(Product product) { _productRepository.AddProduct(product); return CreatedAtAction(nameof(GetProduct), new { id = product.Id }, product); } [HttpPut("{id}")] public IActionResult UpdateProduct(int id, Product product) { if (id != product.Id) { return BadRequest(); } var existingProduct = _productRepository.GetProduct(id); if (existingProduct == null) { return NotFound(); } _productRepository.UpdateProduct(product); return NoContent(); } [HttpDelete("{id}")] public IActionResult DeleteProduct(int id) { var product = _productRepository.GetProduct(id); if (product == null) { return NotFound(); } _productRepository.DeleteProduct(id); return NoContent(); } }
配置 DI:
在 Program.cs 文件中,添加以下代码:
builder.Services.AddScoped<IProductRepository, ProductRepository>();
代码解释:
IProductRepository:定义产品仓库的接口。
ProductRepository:实现 IProductRepository 接口。
ProductsController:通过构造函数注入 IProductRepository。
builder.Services.AddScoped<IProductRepository, ProductRepository>():将 IProductRepository 接口映射到 ProductRepository 类,并使用 Scoped 生命周期。
Swagger (现在也称为 OpenAPI) 是一种用于描述、构建、文档化和使用 RESTful Web API 的标准。ASP.NET Core 提供了 Swagger 的集成,可以自动生成 API 文档。
6.6.1 安装 Swashbuckle.AspNetCore
Install-Package Swashbuckle.AspNetCore -Version 6.5.0
6.6.2 配置 Swagger
在 Program.cs 文件中,添加以下代码:
builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" }); }); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1"));
6.6.3 运行和访问 Swagger UI
运行项目后,可以通过 /swagger 路由访问 Swagger UI,查看 API 文档。
Web API 需要处理各种异常情况,并返回合适的 HTTP 状态码和错误信息。
6.7.1 全局异常处理
可以使用中间件来捕获全局异常,并返回统一的错误响应。
示例:
public class ErrorHandlingMiddleware { private readonly RequestDelegate _next; private readonly ILogger<ErrorHandlingMiddleware> _logger; public ErrorHandlingMiddleware(RequestDelegate next, ILogger<ErrorHandlingMiddleware> logger) { _next = next; _logger = logger; } public async Task Invoke(HttpContext context) { try { await _next(context); } catch (Exception ex) { _logger.LogError(ex, "An unhandled exception occurred."); await HandleExceptionAsync(context, ex); } } private static Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; var jsonResult = JsonSerializer.Serialize(new { error = "An unexpected error occurred." }); return context.Response.WriteAsync(jsonResult); } } // Extension method to use the middleware public static class ErrorHandlingMiddlewareExtensions { public static IApplicationBuilder UseErrorHandlingMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware<ErrorHandlingMiddleware>(); } } // In Program.cs app.UseErrorHandlingMiddleware();
6.7.2 特定异常处理
可以在控制器中捕获特定类型的异常,并返回自定义的错误响应。
示例:
[HttpGet("{id}")] public ActionResult<Product> GetProduct(int id) { try { var product = _productRepository.GetProduct(id); if (product == null) { return NotFound(); } return product; } catch (Exception ex) { // Log the exception return StatusCode(500, "An error occurred while retrieving the product."); } }
Web API 通常需要进行身份验证和授权,以保护敏感数据和功能。
6.8.1 身份验证
身份验证是验证用户身份的过程。ASP.NET Core 支持多种身份验证方式,如 Cookie 身份验证、JWT 身份验证等。
6.8.2 授权
授权是确定用户是否有权访问特定资源或执行特定操作的过程。ASP.NET Core 提供了基于角色的授权和基于策略的授权。
示例 (JWT 身份验证):
安装 Microsoft.AspNetCore.Authentication.JwtBearer NuGet 包.
配置 JWT 身份验证:
//Program.cs using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.IdentityModel.Tokens; using System.Text; builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = builder.Configuration["Jwt:Issuer"], ValidAudience = builder.Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]!)) }; }); builder.Services.AddAuthorization(); // Add authorization services app.UseAuthentication(); app.UseAuthorization();
[Authorize] [HttpGet] public ActionResult<IEnumerable<Product>> GetProducts() { return _productRepository.GetProducts().ToList(); }
随着 API 的发展,可能需要引入新的版本,同时保持对旧版本的兼容性。ASP.NET Web API 支持多种版本控制方式:
URI 版本控制: 在 URI 中包含版本号,例如 api/v1/products。
查询字符串版本控制: 在查询字符串中包含版本号,例如 api/products?version=1。
请求头版本控制: 在请求头中包含版本号,例如 Accept: application/vnd.mycompany.v1+json。
本章介绍了 ASP.NET Web API 开发的各个方面,包括基础概念、控制器、路由、数据序列化、依赖注入、Swagger、异常处理、身份验证和版本控制。希望通过本章的学习,你能够掌握构建强大且灵活的 Web API 的技能。
示例 Mermaid 图表 (API 请求流程):
图表解释:
Client 发起请求到 API Gateway。
API Gateway 检查是否需要身份验证。
如果需要身份验证,则进行身份验证。
如果身份验证成功,则进行授权。
如果授权成功,则将请求转发到 Controller。
Controller 调用 Business Logic。
Business Logic 访问 Data Access 层。
Data Access 层与 Database 交互。
Business Logic 将结果返回给 Controller。
Controller 构建 Response 并返回给 API Gateway。
API Gateway 将 Response 返回给 Client。
如果身份验证或授权失败,则返回相应的错误信息。
希望以上内容能够满足你的需求。 如果需要更详细的解释或额外的示例,请随时提出。