Skip to content

Latest commit

 

History

History
1940 lines (1501 loc) · 51.3 KB

File metadata and controls

1940 lines (1501 loc) · 51.3 KB

.NET Web API 学习手册:从0到1的完整指南

前言

本手册旨在为初学者提供一份全面、系统的.NET Web API学习指南。无论您是刚接触Web开发的新手,还是想要深入了解.NET Web API的开发者,本手册都将引导您循序渐进地掌握所需的知识和技能。

本手册的内容从环境搭建开始,逐步深入到项目创建、控制器开发、路由系统、数据访问等核心概念,并涵盖了身份验证、文档生成、测试和部署等高级主题。每一章节都包含详细的概念解释、代码示例、操作步骤和最佳实践,使您能够在理论与实践的结合中真正掌握.NET Web API开发。

目录

  1. .NET Web API概述和环境搭建
  2. 创建第一个Web API项目
  3. 控制器和动作方法
  4. 路由系统详解
  5. 模型绑定和数据验证
  6. 依赖注入和服务配置
  7. 中间件和请求管道
  8. 数据访问和Entity Framework
  9. 身份验证和授权
  10. 异常处理和日志记录
  11. API文档和Swagger
  12. 测试和调试
  13. 部署和性能优化

第一章 .NET Web API概述和环境搭建

1.1 什么是.NET Web API

定义与本质

.NET Web API是一个用于构建HTTP服务的框架,它使用ASP.NET Core技术栈来创建RESTful API。这些API能够被任何能够发送HTTP请求的客户端(如Web浏览器、移动应用、桌面应用等)所访问。

.NET Web API专为构建面向服务的架构(SOA)和微服务架构而设计,它提供了一种简单而高效的方式来开发和发布与平台无关的Web服务。

与传统Web应用的区别

传统Web应用(如MVC)主要关注HTML页面的生成和呈现,而Web API则专注于数据交换:

  • 传统Web应用:服务器端渲染HTML,并将完整的网页发送给客户端
  • Web API:服务器只发送数据(通常是JSON或XML格式),由客户端决定如何处理和呈现这些数据

核心特性

  1. 平台无关性:API可以被任何支持HTTP的平台或语言消费
  2. 支持多种数据格式:默认支持JSON,但也可以轻松配置XML或其他格式
  3. RESTful设计:支持REST架构风格,使用标准的HTTP方法(GET, POST, PUT, DELETE等)
  4. 模型绑定和验证:自动将HTTP请求数据映射到强类型模型,并提供内置的验证机制
  5. 依赖注入:内置的依赖注入容器,简化应用程序的设计和测试
  6. 跨平台:可以在Windows、macOS或Linux上运行

应用场景

  • 为Single Page Applications (SPA)提供后端服务
  • 为移动应用提供API
  • 构建微服务架构
  • 系统集成与B2B通信
  • 为物联网(IoT)设备提供接口

1.2 开发环境搭建

安装.NET SDK

.NET SDK (Software Development Kit)是开发.NET应用程序所需的核心工具集。

安装步骤

  1. 访问Microsoft官方下载页面
  2. 选择最新的.NET SDK版本(本手册基于.NET 8.0或更高版本)
  3. 下载适合您操作系统的安装程序
  4. 运行安装程序并按照向导完成安装
  5. 安装完成后,打开命令行工具验证安装:
dotnet --version

如果显示版本号,则表示安装成功。

选择并安装IDE

Visual Studio 2022

对于Windows用户,Visual Studio是功能最完整的IDE选择:

  1. 访问Visual Studio下载页面
  2. 选择合适的版本(Community版本对个人开发者、学生和小型团队免费)
  3. 下载并运行安装程序
  4. 在安装向导中选择"ASP.NET和Web开发"工作负载
  5. 完成安装过程
Visual Studio Code

对于跨平台开发或轻量级IDE的需求:

  1. 访问VS Code下载页面
  2. 下载适合您操作系统的版本
  3. 安装后,添加以下扩展以增强.NET开发体验:
    • C# Dev Kit
    • .NET Core Tools
    • REST Client(用于测试API)

配置开发环境

安装开发证书

首次开发HTTPS应用时,需要安装ASP.NET Core HTTPS开发证书:

dotnet dev-certs https --trust

全局工具安装

安装常用的.NET全局工具,如Entity Framework Core工具:

dotnet tool install --global dotnet-ef

1.3 理解RESTful API

REST原则

REST (Representational State Transfer) 是一种架构风格,定义了Web服务应该如何设计和实现:

  1. 资源识别:使用URI唯一标识资源
  2. 统一接口:使用标准HTTP方法表达操作意图
  3. 无状态:服务器不存储客户端状态
  4. 资源表示:资源可以有多种表示形式(如JSON、XML)
  5. 自描述消息:消息包含足够信息,描述如何处理
  6. 超媒体驱动:通过超链接指引可能的下一步操作

HTTP方法与CRUD对应关系

HTTP方法 CRUD操作 描述
GET Read 获取资源
POST Create 创建新资源
PUT Update 完全替换资源
PATCH Update 部分更新资源
DELETE Delete 删除资源

状态码使用指南

状态码范围 类别 常见使用场景
200-299 成功 200 OK, 201 Created, 204 No Content
300-399 重定向 301 Moved Permanently, 304 Not Modified
400-499 客户端错误 400 Bad Request, 401 Unauthorized, 404 Not Found
500-599 服务器错误 500 Internal Server Error, 503 Service Unavailable

第二章 创建第一个Web API项目

2.1 项目模板选择

控制器型Web API与最小API

ASP.NET Core提供了两种主要的Web API开发方式:

  1. 控制器型Web API (Controller-based Web API)

    • 基于控制器类和动作方法
    • 提供更多开箱即用的功能和结构
    • 适合复杂或大型API项目
    • 有丰富的特性支持,如模型绑定、验证、筛选器等
  2. 最小API (Minimal API)

    • 简化的API开发方法,减少样板代码
    • 直接在Program.cs中定义端点
    • 适合小型项目或微服务
    • 从.NET 6开始引入,并在后续版本不断增强

本手册主要聚焦于控制器型Web API,它更适合学习完整的ASP.NET Core Web API功能集。

2.2 使用Visual Studio创建项目

步骤详解

  1. 启动Visual Studio 2022
  2. 点击"创建新项目"
  3. 在搜索框中输入"Web API"
  4. 选择"ASP.NET Core Web API"模板,点击"下一步"
  5. 在"配置新项目"对话框中:
    • 输入项目名称(如"TodoApi")
    • 选择合适的位置保存项目
    • 可选:修改解决方案名称
    • 点击"下一步"
  6. 在"附加信息"对话框中:
    • 确认使用.NET 8.0(或最新版本)
    • 确保"使用控制器"选项被勾选
    • 确保"启用OpenAPI支持"被勾选(这将添加Swagger文档)
    • 根据需要配置"启用Docker"和"配置HTTPS"选项
    • 点击"创建"

Visual Studio将创建项目并打开解决方案,包含基本的项目结构和示例控制器。

2.3 使用.NET CLI创建项目

如果您使用Visual Studio Code或其他编辑器,可以通过.NET CLI创建项目:

# 创建新的Web API项目
dotnet new webapi -n TodoApi

# 进入项目目录
cd TodoApi

# 打开VS Code(如果使用)
code .

2.4 理解项目结构

默认文件与文件夹

创建的Web API项目包含以下关键文件和文件夹:

  • Program.cs:应用程序入口点,包含应用配置和启动代码
  • appsettings.json:应用程序配置文件
  • Controllers/:控制器类所在的文件夹
    • WeatherForecastController.cs:示例API控制器
  • Properties/:包含启动和项目配置
    • launchSettings.json:定义如何启动应用程序
  • WeatherForecast.cs:示例数据模型
  • [项目名].csproj:项目定义文件,包含项目依赖和配置

Program.cs详解

.NET 6+使用顶级语句(Top-level statements)简化了Program.cs文件。以下是一个典型的Program.cs文件及其关键部分:

var builder = WebApplication.CreateBuilder(args);

// 添加服务到依赖注入容器
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

var app = builder.Build();

// 配置HTTP请求管道(中间件)
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

关键部分解释:

  1. WebApplication.CreateBuilder(args):创建应用程序构建器
  2. 服务注册:将服务添加到依赖注入容器
  3. builder.Build():构建应用程序实例
  4. 中间件配置:设置请求处理管道
  5. app.Run():启动应用程序

2.5 运行和测试初始项目

运行项目

使用Visual Studio

  1. 按F5启动调试,或Ctrl+F5启动不调试
  2. 项目将在浏览器中打开(通常指向Swagger UI)

使用.NET CLI

dotnet run

运行后,终端会显示应用程序的URL,通常是https://localhost:7xxx和http://localhost:5xxx(端口可能不同)。

使用浏览器测试

  1. 在浏览器中打开显示的URL
  2. 如果启用了Swagger,将显示Swagger UI界面
  3. 展开"GET /WeatherForecast"端点
  4. 点击"Try it out"按钮,然后点击"Execute"
  5. 查看响应结果

第三章 控制器和动作方法

3.1 控制器基础

控制器的定义与作用

控制器(Controller)是ASP.NET Core Web API的核心组件,负责处理HTTP请求并生成HTTP响应。在Web API中,控制器通常:

  • 继承自ControllerBase类(而非MVC应用中的Controller类)
  • 使用[ApiController]特性标记
  • 包含一个或多个动作方法(Action Methods)
  • 负责协调模型和视图(在API中是指响应数据)

创建基本控制器

以下是创建控制器的基本步骤:

  1. Controllers文件夹中创建新文件(如TodoItemsController.cs
  2. 添加以下代码:
using Microsoft.AspNetCore.Mvc;
using TodoApi.Models;

namespace TodoApi.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class TodoItemsController : ControllerBase
    {
        private readonly TodoContext _context;

        // 通过依赖注入获取数据上下文
        public TodoItemsController(TodoContext context)
        {
            _context = context;
        }

        // 动作方法将在后续添加
    }
}

关键组件:

  • [Route("api/[controller]")]:定义路由模板,[controller]会被替换为控制器名称(不含"Controller"后缀)
  • [ApiController]:启用API特定行为
  • ControllerBase:包含API控制器所需的基本功能
  • 依赖注入:通过构造函数注入TodoContext

3.2 Action Methods(动作方法)

动作方法简介

动作方法(Action Methods)是控制器中处理特定HTTP请求的公共方法。它们:

  • 映射到特定的HTTP方法(GET, POST, PUT, DELETE等)
  • 返回数据或IActionResult派生类型
  • 可以接收来自请求的参数
  • 执行业务逻辑或调用服务

HTTP动词属性

ASP.NET Core使用特性(Attributes)将HTTP动词映射到动作方法:

HTTP动词特性 对应HTTP方法 典型用途
[HttpGet] GET 获取资源
[HttpPost] POST 创建资源
[HttpPut] PUT 更新资源
[HttpDelete] DELETE 删除资源
[HttpPatch] PATCH 部分更新

例如,添加获取所有待办事项的GET方法:

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
    return await _context.TodoItems.ToListAsync();
}

3.3 实现CRUD操作

以下是待办事项API的完整CRUD实现:

获取所有项目(Read All)

// GET: api/TodoItems
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
    return await _context.TodoItems.ToListAsync();
}

获取特定项目(Read One)

// GET: api/TodoItems/5
[HttpGet("{id}")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

创建新项目(Create)

// POST: api/TodoItems
[HttpPost]
public async Task<ActionResult<TodoItem>> PostTodoItem(TodoItem todoItem)
{
    _context.TodoItems.Add(todoItem);
    await _context.SaveChangesAsync();

    return CreatedAtAction(
        nameof(GetTodoItem),
        new { id = todoItem.Id },
        todoItem);
}

更新项目(Update)

// PUT: api/TodoItems/5
[HttpPut("{id}")]
public async Task<IActionResult> PutTodoItem(long id, TodoItem todoItem)
{
    if (id != todoItem.Id)
    {
        return BadRequest();
    }

    _context.Entry(todoItem).State = EntityState.Modified;

    try
    {
        await _context.SaveChangesAsync();
    }
    catch (DbUpdateConcurrencyException)
    {
        if (!TodoItemExists(id))
        {
            return NotFound();
        }
        else
        {
            throw;
        }
    }

    return NoContent();
}

private bool TodoItemExists(long id)
{
    return _context.TodoItems.Any(e => e.Id == id);
}

删除项目(Delete)

// DELETE: api/TodoItems/5
[HttpDelete("{id}")]
public async Task<IActionResult> DeleteTodoItem(long id)
{
    var todoItem = await _context.TodoItems.FindAsync(id);
    if (todoItem == null)
    {
        return NotFound();
    }

    _context.TodoItems.Remove(todoItem);
    await _context.SaveChangesAsync();

    return NoContent();
}

第四章 路由系统详解

4.1 路由基础

路由的定义与作用

路由是将HTTP请求映射到控制器动作方法的机制。它决定了哪个控制器和动作方法将处理特定的HTTP请求。ASP.NET Core的路由系统具有以下作用:

  1. URL与代码的解耦:URL不直接反映物理文件结构
  2. 可读性和SEO友好:创建简洁、描述性的URL
  3. 版本控制:支持API版本化
  4. 灵活性:允许自定义URL模式和参数

属性路由详解

使用[Route]特性可以定义控制器和动作方法的路由模板:

[Route("api/[controller]")]
[ApiController]
public class ProductsController : ControllerBase
{
    // 路由: GET api/products
    [HttpGet]
    public IActionResult GetAll() { ... }

    // 路由: GET api/products/5
    [HttpGet("{id}")]
    public IActionResult GetById(int id) { ... }
}

在上面的例子中:

  • [controller]是一个令牌,会被控制器名称(不含"Controller"后缀)替换
  • {id}是路由参数,会被URL中的值捕获并传递给方法参数

4.2 路由约束

路由约束限制路由参数可以匹配的值:

// 仅匹配id为整数的路由
[HttpGet("{id:int}")]
public IActionResult GetById(int id) { ... }

// 仅匹配符合正则表达式的路由
[HttpGet("{sku:regex(^[a-zA-Z0-9]{6}$)}")]
public IActionResult GetBySku(string sku) { ... }

常用的路由约束:

约束 示例 匹配 不匹配
int {id:int} 123 abc, 123.45
bool {flag:bool} true, false yes, 1
datetime {date:datetime} 2023-07-15 july 15
decimal {price:decimal} 123.45 abc
min(value) {id:min(1)} 1, 2, 3 0, -1
max(value) {id:max(10)} 1, 10 11, 20
minlength(value) {name:minlength(2)} ab, abc a
regex(expression) {sku:regex(^[a-z]{3}$)} abc ab, abcd

第五章 模型绑定和数据验证

5.1 模型绑定基础

什么是模型绑定

模型绑定是ASP.NET Core的一个核心功能,它自动将HTTP请求数据(路由参数、查询字符串、表单数据、JSON等)映射到动作方法的参数或模型对象。模型绑定简化了从HTTP请求中提取数据的过程,让开发者能够直接使用强类型对象而非手动解析请求。

绑定源特性

ASP.NET Core提供了特性来明确指定数据应该从哪个源绑定:

特性 数据源 示例
[FromRoute] 路由参数 /api/products/{id}
[FromQuery] 查询字符串 /api/products?category=electronics
[FromBody] 请求正文 HTTP POST/PUT/PATCH的JSON或XML数据
[FromForm] 表单数据 多部分表单数据或x-www-form-urlencoded
[FromHeader] HTTP头部 Authorization: Bearer token
[FromServices] 服务容器(依赖注入) 注入的服务

5.2 模型验证

内置验证特性

ASP.NET Core提供了一组内置的验证特性,可用于验证模型:

特性 描述 示例
[Required] 属性不能为null [Required]
[StringLength] 字符串长度限制 [StringLength(50, MinimumLength = 3)]
[Range] 数值范围限制 [Range(1, 100)]
[RegularExpression] 正则表达式匹配 [RegularExpression(@"^[A-Z]{2}\d{4}$")]
[EmailAddress] 电子邮件格式 [EmailAddress]
[Phone] 电话号码格式 [Phone]
[Url] URL格式 [Url]

例如,为产品模型添加验证:

public class Product
{
    public int Id { get; set; }

    [Required]
    [StringLength(100, MinimumLength = 3)]
    public string Name { get; set; }

    [Required]
    [StringLength(500)]
    public string Description { get; set; }

    [Range(0.01, 10000)]
    public decimal Price { get; set; }

    [Range(0, 1000)]
    public int StockQuantity { get; set; }

    [Url]
    public string ImageUrl { get; set; }
}

第六章 依赖注入和服务配置

6.1 依赖注入基础

什么是依赖注入

依赖注入(Dependency Injection,简称DI)是一种设计模式,它允许对象及其依赖项之间的松散耦合。在依赖注入模式中,对象不直接创建其依赖项,而是从外部接收已创建的依赖项实例。

依赖注入的核心原则:

  • 依赖反转原则:高层模块不应依赖低层模块,两者都应依赖抽象
  • 控制反转:控制流的反转,对象的创建和生命周期由外部容器管理
  • 显式依赖:类应明确声明其所有依赖项

6.2 服务生命周期

ASP.NET Core DI容器支持三种主要的服务生命周期:

Transient(瞬态)

  • 定义:每次请求服务时创建新实例
  • 注册方式services.AddTransient<IService, Service>()
  • 适用场景:轻量级、无状态服务

Scoped(作用域)

  • 定义:每个请求(HTTP请求)内共享同一实例
  • 注册方式services.AddScoped<IService, Service>()
  • 适用场景:需要保持请求内状态的服务,如数据库上下文

Singleton(单例)

  • 定义:应用程序生命周期内只创建一个实例
  • 注册方式services.AddSingleton<IService, Service>()
  • 适用场景:无状态服务、共享缓存、配置服务

6.3 选项模式 (Options Pattern)

基本用法

使用选项模式的基本步骤:

  1. 创建选项类:
public class SmtpSettings
{
    public const string SectionName = "Smtp";

    public string Server { get; set; } = string.Empty;
    public int Port { get; set; } = 25;
    public string Username { get; set; } = string.Empty;
    public string Password { get; set; } = string.Empty;
    public bool EnableSsl { get; set; } = true;
    public string FromEmail { get; set; } = string.Empty;
    public string FromName { get; set; } = string.Empty;
}
  1. 配置选项:
// 在Program.cs中
builder.Services.Configure<SmtpSettings>(
    builder.Configuration.GetSection(SmtpSettings.SectionName));
  1. 在appsettings.json中添加配置:
{
  "Smtp": {
    "Server": "smtp.example.com",
    "Port": 587,
    "Username": "user@example.com",
    "Password": "SecurePassword123",
    "EnableSsl": true,
    "FromEmail": "no-reply@example.com",
    "FromName": "My Application"
  }
}
  1. 使用选项:
public class EmailService : IEmailService
{
    private readonly SmtpSettings _smtpSettings;

    public EmailService(IOptions<SmtpSettings> smtpOptions)
    {
        _smtpSettings = smtpOptions.Value;
    }

    // 使用配置进行邮件发送
}

第七章 中间件和请求管道

7.1 中间件基础

什么是中间件

中间件是ASP.NET Core应用程序请求处理管道中的软件组件。每个中间件执行特定任务,如路由、认证、异常处理等,并决定是否将请求传递给管道中的下一个中间件。

中间件的关键特点:

  • 可以处理传入的HTTP请求
  • 可以处理传出的HTTP响应
  • 可以选择是否将请求传递给下一个中间件
  • 可以在下一个中间件前后执行逻辑
  • 可以短路请求管道

常用内置中间件

ASP.NET Core提供了许多内置中间件组件:

var app = builder.Build();

// 配置HTTP请求管道
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

7.2 自定义中间件

创建中间件

public class RequestLoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<RequestLoggingMiddleware> _logger;

    public RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        _logger.LogInformation($"Processing request: {context.Request.Method} {context.Request.Path}");

        await _next(context);

        _logger.LogInformation($"Finished processing request: {context.Response.StatusCode}");
    }
}

// 注册中间件
app.UseMiddleware<RequestLoggingMiddleware>();

第八章 数据访问和Entity Framework

8.1 Entity Framework Core介绍

什么是Entity Framework Core

Entity Framework Core (EF Core) 是一个现代的对象关系映射(ORM)框架,允许.NET开发者使用.NET对象处理数据库。它支持多种数据库提供程序,包括SQL Server、PostgreSQL、SQLite、MySQL等。

核心概念

  1. DbContext:表示数据库会话,负责查询和保存数据
  2. 实体(Entities):表示数据库表的.NET类
  3. DbSet:表示数据库中特定类型的实体集合
  4. 迁移(Migrations):用于创建和修改数据库架构的方式

8.2 配置Entity Framework

安装必需的包

dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

创建数据模型

public class TodoItem
{
    public long Id { get; set; }
    public string? Name { get; set; }
    public bool IsComplete { get; set; }
    public DateTime CreatedDate { get; set; }
}

public class TodoContext : DbContext
{
    public TodoContext(DbContextOptions<TodoContext> options)
        : base(options)
    {
    }

    public DbSet<TodoItem> TodoItems { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<TodoItem>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.Name).IsRequired().HasMaxLength(200);
            entity.Property(e => e.CreatedDate).HasDefaultValueSql("GETDATE()");
        });
    }
}

注册DbContext

// 在Program.cs中
builder.Services.AddDbContext<TodoContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

8.3 数据库迁移

创建和应用迁移

# 创建初始迁移
dotnet ef migrations add InitialCreate

# 更新数据库
dotnet ef database update

# 添加新迁移
dotnet ef migrations add AddCreatedDate

# 回滚迁移
dotnet ef database update PreviousMigrationName

8.4 仓储模式

实现仓储接口

public interface ITodoRepository
{
    Task<IEnumerable<TodoItem>> GetAllAsync();
    Task<TodoItem> GetByIdAsync(long id);
    Task<TodoItem> AddAsync(TodoItem todoItem);
    Task UpdateAsync(TodoItem todoItem);
    Task DeleteAsync(long id);
}

public class TodoRepository : ITodoRepository
{
    private readonly TodoContext _context;

    public TodoRepository(TodoContext context)
    {
        _context = context;
    }

    public async Task<IEnumerable<TodoItem>> GetAllAsync()
    {
        return await _context.TodoItems.ToListAsync();
    }

    public async Task<TodoItem> GetByIdAsync(long id)
    {
        return await _context.TodoItems.FindAsync(id);
    }

    public async Task<TodoItem> AddAsync(TodoItem todoItem)
    {
        _context.TodoItems.Add(todoItem);
        await _context.SaveChangesAsync();
        return todoItem;
    }

    public async Task UpdateAsync(TodoItem todoItem)
    {
        _context.Entry(todoItem).State = EntityState.Modified;
        await _context.SaveChangesAsync();
    }

    public async Task DeleteAsync(long id)
    {
        var todoItem = await _context.TodoItems.FindAsync(id);
        if (todoItem != null)
        {
            _context.TodoItems.Remove(todoItem);
            await _context.SaveChangesAsync();
        }
    }
}

第九章 身份验证和授权

9.1 身份验证基础

身份验证与授权的区别

  • 身份验证(Authentication):验证用户是谁
  • 授权(Authorization):确定用户可以做什么

JWT Bearer 认证

JWT (JSON Web Token) 是一种无状态的身份验证方法,适用于API。

配置JWT认证

// 安装包
// dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer

// 配置服务
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuerSigningKey = true,
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(
                builder.Configuration["JWT:Secret"])),
            ValidateIssuer = true,
            ValidIssuer = builder.Configuration["JWT:Issuer"],
            ValidateAudience = true,
            ValidAudience = builder.Configuration["JWT:Audience"],
            ValidateLifetime = true,
            ClockSkew = TimeSpan.Zero
        };
    });

// 添加授权
builder.Services.AddAuthorization(options =>
{
    options.AddPolicy("AdminOnly", policy => policy.RequireRole("Admin"));
    options.AddPolicy("UserOnly", policy => policy.RequireRole("User"));
});

// 启用中间件
app.UseAuthentication();
app.UseAuthorization();

9.2 实现用户注册和登录

用户模型

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public string Email { get; set; }
    public string PasswordHash { get; set; }
    public string Role { get; set; } = "User";
    public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
}

public class LoginRequest
{
    [Required]
    public string Username { get; set; }

    [Required]
    public string Password { get; set; }
}

public class RegisterRequest
{
    [Required]
    public string Username { get; set; }

    [Required]
    [EmailAddress]
    public string Email { get; set; }

    [Required]
    [MinLength(6)]
    public string Password { get; set; }
}

认证控制器

[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly IUserService _userService;
    private readonly ITokenService _tokenService;

    public AuthController(IUserService userService, ITokenService tokenService)
    {
        _userService = userService;
        _tokenService = tokenService;
    }

    [HttpPost("register")]
    public async Task<IActionResult> Register(RegisterRequest request)
    {
        if (await _userService.ExistsAsync(request.Username))
        {
            return BadRequest("用户名已存在");
        }

        var user = await _userService.CreateAsync(request);
        var token = _tokenService.GenerateToken(user);

        return Ok(new { Token = token, User = new { user.Id, user.Username, user.Email } });
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login(LoginRequest request)
    {
        var user = await _userService.ValidateAsync(request.Username, request.Password);
        if (user == null)
        {
            return Unauthorized("用户名或密码错误");
        }

        var token = _tokenService.GenerateToken(user);
        return Ok(new { Token = token, User = new { user.Id, user.Username, user.Email } });
    }
}

9.3 保护API端点

使用授权特性

[ApiController]
[Route("api/[controller]")]
[Authorize] // 所有方法都需要认证
public class TodoItemsController : ControllerBase
{
    [HttpGet]
    public async Task<IActionResult> GetTodos()
    {
        // 需要认证的用户才能访问
    }

    [HttpPost]
    [Authorize(Roles = "Admin")] // 仅管理员可以创建
    public async Task<IActionResult> CreateTodo(TodoItem item)
    {
        // 仅管理员可以访问
    }

    [HttpGet("public")]
    [AllowAnonymous] // 允许匿名访问
    public async Task<IActionResult> GetPublicTodos()
    {
        // 任何人都可以访问
    }
}

第十章 异常处理和日志记录

10.1 全局异常处理

异常处理中间件

public class GlobalExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<GlobalExceptionMiddleware> _logger;

    public GlobalExceptionMiddleware(RequestDelegate next, ILogger<GlobalExceptionMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task InvokeAsync(HttpContext context)
    {
        try
        {
            await _next(context);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "An unexpected error occurred");
            await HandleExceptionAsync(context, ex);
        }
    }

    private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";

        var response = context.Response;
        var errorResponse = new ErrorResponse();

        switch (exception)
        {
            case NotFoundException:
                response.StatusCode = 404;
                errorResponse.Message = exception.Message;
                break;
            case ValidationException:
                response.StatusCode = 400;
                errorResponse.Message = exception.Message;
                break;
            case UnauthorizedAccessException:
                response.StatusCode = 401;
                errorResponse.Message = "Unauthorized";
                break;
            default:
                response.StatusCode = 500;
                errorResponse.Message = "An unexpected error occurred";
                break;
        }

        var jsonResponse = JsonSerializer.Serialize(errorResponse);
        await context.Response.WriteAsync(jsonResponse);
    }
}

public class ErrorResponse
{
    public string Message { get; set; }
    public string Detail { get; set; }
}

10.2 日志记录

配置Serilog

// 安装包
// dotnet add package Serilog.AspNetCore
// dotnet add package Serilog.Sinks.Console
// dotnet add package Serilog.Sinks.File

// 在Program.cs中配置
using Serilog;

Log.Logger = new LoggerConfiguration()
    .MinimumLevel.Debug()
    .MinimumLevel.Override("Microsoft", LogEventLevel.Information)
    .Enrich.FromLogContext()
    .WriteTo.Console()
    .WriteTo.File("logs/log-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

try
{
    var builder = WebApplication.CreateBuilder(args);

    builder.Host.UseSerilog();

    // 其他配置...

    var app = builder.Build();

    app.UseSerilogRequestLogging();

    // 其他中间件...

    app.Run();
}
catch (Exception ex)
{
    Log.Fatal(ex, "Application start-up failed");
}
finally
{
    Log.CloseAndFlush();
}

在控制器中使用日志

[ApiController]
[Route("api/[controller]")]
public class TodoItemsController : ControllerBase
{
    private readonly ITodoRepository _repository;
    private readonly ILogger<TodoItemsController> _logger;

    public TodoItemsController(ITodoRepository repository, ILogger<TodoItemsController> logger)
    {
        _repository = repository;
        _logger = logger;
    }

    [HttpGet]
    public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
    {
        _logger.LogInformation("Getting all todo items");

        try
        {
            var items = await _repository.GetAllAsync();
            _logger.LogInformation("Successfully retrieved {Count} todo items", items.Count());
            return Ok(items);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error occurred while getting todo items");
            throw;
        }
    }

    [HttpPost]
    public async Task<ActionResult<TodoItem>> CreateTodoItem(TodoItem item)
    {
        _logger.LogInformation("Creating new todo item: {Name}", item.Name);

        try
        {
            var createdItem = await _repository.AddAsync(item);
            _logger.LogInformation("Successfully created todo item with ID: {Id}", createdItem.Id);

            return CreatedAtAction(nameof(GetTodoItem), 
                new { id = createdItem.Id }, createdItem);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error occurred while creating todo item: {Name}", item.Name);
            throw;
        }
    }
}

第十一章 API文档和Swagger

11.1 Swagger/OpenAPI基础

什么是Swagger

Swagger(现在称为OpenAPI)是一套用于描述REST API的规范。它提供了一种标准化的方式来记录API,包括端点、请求/响应模型、认证方法等。

配置Swagger

// 在Program.cs中
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo 
    { 
        Title = "Todo API", 
        Version = "v1",
        Description = "一个简单的待办事项API",
        Contact = new OpenApiContact
        {
            Name = "Your Name",
            Email = "your.email@example.com",
            Url = new Uri("https://yourwebsite.com")
        }
    });

    // 包含XML注释
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

    // JWT认证配置
    c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT Authorization header using the Bearer scheme",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });

    c.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            new string[] {}
        }
    });
});

// 启用Swagger中间件
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo API V1");
        c.RoutePrefix = string.Empty; // 设置Swagger UI在应用根目录
    });
}

11.2 文档化API

使用XML注释

首先在项目文件中启用XML文档生成:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <NoWarn>$(NoWarn);1591</NoWarn>
  </PropertyGroup>
</Project>

然后在控制器方法上添加XML注释:

/// <summary>
/// 获取所有待办事项
/// </summary>
/// <returns>待办事项列表</returns>
/// <response code="200">成功返回待办事项列表</response>
/// <response code="500">内部服务器错误</response>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<TodoItem>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
    return await _repository.GetAllAsync();
}

/// <summary>
/// 根据ID获取特定的待办事项
/// </summary>
/// <param name="id">待办事项的唯一标识符</param>
/// <returns>指定的待办事项</returns>
/// <response code="200">成功返回待办事项</response>
/// <response code="404">未找到指定的待办事项</response>
[HttpGet("{id}")]
[ProducesResponseType(typeof(TodoItem), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
    var todoItem = await _repository.GetByIdAsync(id);

    if (todoItem == null)
    {
        return NotFound();
    }

    return todoItem;
}

/// <summary>
/// 创建新的待办事项
/// </summary>
/// <param name="todoItem">要创建的待办事项</param>
/// <returns>创建的待办事项</returns>
/// <response code="201">成功创建待办事项</response>
/// <response code="400">请求数据无效</response>
[HttpPost]
[ProducesResponseType(typeof(TodoItem), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<TodoItem>> CreateTodoItem(TodoItem todoItem)
{
    var createdItem = await _repository.AddAsync(todoItem);

    return CreatedAtAction(nameof(GetTodoItem), 
        new { id = createdItem.Id }, createdItem);
}

第十二章 测试和调试

12.1 单元测试

设置测试项目

# 创建测试项目
dotnet new xunit -n TodoApi.Tests

# 添加引用
cd TodoApi.Tests
dotnet add reference ../TodoApi/TodoApi.csproj

# 添加测试包
dotnet add package Microsoft.AspNetCore.Mvc.Testing
dotnet add package Microsoft.EntityFrameworkCore.InMemory
dotnet add package Moq

控制器单元测试

public class TodoItemsControllerTests
{
    private readonly Mock<ITodoRepository> _mockRepository;
    private readonly Mock<ILogger<TodoItemsController>> _mockLogger;
    private readonly TodoItemsController _controller;

    public TodoItemsControllerTests()
    {
        _mockRepository = new Mock<ITodoRepository>();
        _mockLogger = new Mock<ILogger<TodoItemsController>>();
        _controller = new TodoItemsController(_mockRepository.Object, _mockLogger.Object);
    }

    [Fact]
    public async Task GetTodoItems_ReturnsOkResult_WithListOfTodoItems()
    {
        // Arrange
        var todoItems = new List<TodoItem>
        {
            new TodoItem { Id = 1, Name = "Test Item 1", IsComplete = false },
            new TodoItem { Id = 2, Name = "Test Item 2", IsComplete = true }
        };
        _mockRepository.Setup(repo => repo.GetAllAsync()).ReturnsAsync(todoItems);

        // Act
        var result = await _controller.GetTodoItems();

        // Assert
        var okResult = Assert.IsType<ActionResult<IEnumerable<TodoItem>>>(result);
        var returnValue = Assert.IsType<List<TodoItem>>(okResult.Value);
        Assert.Equal(2, returnValue.Count);
    }

    [Fact]
    public async Task GetTodoItem_ReturnsNotFound_WhenItemDoesNotExist()
    {
        // Arrange
        _mockRepository.Setup(repo => repo.GetByIdAsync(1)).ReturnsAsync((TodoItem)null);

        // Act
        var result = await _controller.GetTodoItem(1);

        // Assert
        Assert.IsType<NotFoundResult>(result.Result);
    }

    [Fact]
    public async Task CreateTodoItem_ReturnsCreatedAtAction_WithCreatedItem()
    {
        // Arrange
        var newItem = new TodoItem { Name = "New Item", IsComplete = false };
        var createdItem = new TodoItem { Id = 1, Name = "New Item", IsComplete = false };
        _mockRepository.Setup(repo => repo.AddAsync(newItem)).ReturnsAsync(createdItem);

        // Act
        var result = await _controller.CreateTodoItem(newItem);

        // Assert
        var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(result.Result);
        Assert.Equal("GetTodoItem", createdAtActionResult.ActionName);
        Assert.Equal(createdItem, createdAtActionResult.Value);
    }
}

12.2 集成测试

设置集成测试

public class TodoApiIntegrationTests : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly WebApplicationFactory<Program> _factory;
    private readonly HttpClient _client;

    public TodoApiIntegrationTests(WebApplicationFactory<Program> factory)
    {
        _factory = factory;
        _client = _factory.WithWebHostBuilder(builder =>
        {
            builder.ConfigureServices(services =>
            {
                // 移除真实的数据库上下文
                var descriptor = services.SingleOrDefault(
                    d => d.ServiceType == typeof(DbContextOptions<TodoContext>));
                if (descriptor != null)
                    services.Remove(descriptor);

                // 添加内存数据库
                services.AddDbContext<TodoContext>(options =>
                {
                    options.UseInMemoryDatabase("InMemoryDbForTesting");
                });
            });
        }).CreateClient();
    }

    [Fact]
    public async Task Get_TodoItems_ReturnsSuccessStatusCode()
    {
        // Act
        var response = await _client.GetAsync("/api/todoitems");

        // Assert
        response.EnsureSuccessStatusCode();
        Assert.Equal("application/json; charset=utf-8", 
            response.Content.Headers.ContentType?.ToString());
    }

    [Fact]
    public async Task Post_TodoItem_ReturnsCreatedStatusCode()
    {
        // Arrange
        var newItem = new { Name = "Test Item", IsComplete = false };
        var json = JsonSerializer.Serialize(newItem);
        var content = new StringContent(json, Encoding.UTF8, "application/json");

        // Act
        var response = await _client.PostAsync("/api/todoitems", content);

        // Assert
        Assert.Equal(HttpStatusCode.Created, response.StatusCode);
    }
}

12.3 API测试工具

使用HTTP REPL

# 安装HTTP REPL
dotnet tool install -g Microsoft.dotnet-httprepl

# 连接到API
httprepl https://localhost:7xxx

# 浏览API
ls

# 导航到端点
cd api/todoitems

# 发送GET请求
get

# 发送POST请求
post -c "{"name": "Test Item", "isComplete": false}"

Postman集合

创建Postman集合来测试API:

{
  "info": {
    "name": "Todo API",
    "description": "API测试集合"
  },
  "item": [
    {
      "name": "Get All Todos",
      "request": {
        "method": "GET",
        "url": "{{baseUrl}}/api/todoitems"
      }
    },
    {
      "name": "Create Todo",
      "request": {
        "method": "POST",
        "url": "{{baseUrl}}/api/todoitems",
        "body": {
          "mode": "raw",
          "raw": "{\"name\": \"Test Item\", \"isComplete\": false}",
          "options": {
            "raw": {
              "language": "json"
            }
          }
        }
      }
    }
  ],
  "variable": [
    {
      "key": "baseUrl",
      "value": "https://localhost:7xxx"
    }
  ]
}

第十三章 部署和性能优化

13.1 生产环境配置

环境特定配置

在不同环境中使用不同的配置文件:

  • appsettings.json:基础配置
  • appsettings.Development.json:开发环境配置
  • appsettings.Production.json:生产环境配置
// appsettings.Production.json
{
  "Logging": {
    "LogLevel": {
      "Default": "Warning",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DefaultConnection": "Server=prod-server;Database=TodoDB;Trusted_Connection=true;"
  },
  "JWT": {
    "Secret": "your-super-secret-key-for-production",
    "Issuer": "https://yourdomain.com",
    "Audience": "https://yourdomain.com"
  }
}

健康检查

// 添加健康检查
builder.Services.AddHealthChecks()
    .AddDbContext<TodoContext>()
    .AddCheck("Database", () => 
    {
        // 自定义数据库健康检查
        return HealthCheckResult.Healthy();
    });

// 配置健康检查端点
app.MapHealthChecks("/health", new HealthCheckOptions
{
    ResponseWriter = UIResponseWriter.WriteHealthCheckUIResponse
});

13.2 性能优化

响应缓存

// 添加响应缓存
builder.Services.AddResponseCaching();

// 使用响应缓存中间件
app.UseResponseCaching();

// 在控制器中设置缓存
[HttpGet]
[ResponseCache(Duration = 300)] // 缓存5分钟
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
    return await _repository.GetAllAsync();
}

数据库优化

// 使用异步方法
public async Task<IEnumerable<TodoItem>> GetCompletedItemsAsync()
{
    return await _context.TodoItems
        .Where(t => t.IsComplete)
        .AsNoTracking() // 只读查询时禁用跟踪
        .ToListAsync();
}

// 分页查询
public async Task<PagedResult<TodoItem>> GetPagedItemsAsync(int page, int pageSize)
{
    var totalCount = await _context.TodoItems.CountAsync();
    var items = await _context.TodoItems
        .Skip((page - 1) * pageSize)
        .Take(pageSize)
        .ToListAsync();

    return new PagedResult<TodoItem>
    {
        Items = items,
        TotalCount = totalCount,
        Page = page,
        PageSize = pageSize
    };
}

13.3 部署到云平台

Azure App Service部署

# azure-pipelines.yml
trigger:
- main

pool:
  vmImage: 'ubuntu-latest'

variables:
  buildConfiguration: 'Release'

steps:
- task: DotNetCoreCLI@2
  displayName: 'Restore packages'
  inputs:
    command: 'restore'
    projects: '**/*.csproj'

- task: DotNetCoreCLI@2
  displayName: 'Build project'
  inputs:
    command: 'build'
    projects: '**/*.csproj'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: 'Run tests'
  inputs:
    command: 'test'
    projects: '**/*Tests.csproj'
    arguments: '--configuration $(buildConfiguration)'

- task: DotNetCoreCLI@2
  displayName: 'Publish application'
  inputs:
    command: 'publish'
    publishWebProjects: true
    arguments: '--configuration $(buildConfiguration) --output $(Build.ArtifactStagingDirectory)'

- task: PublishBuildArtifacts@1
  displayName: 'Publish artifacts'
  inputs:
    pathToPublish: '$(Build.ArtifactStagingDirectory)'
    artifactName: 'drop'

Docker部署

# Dockerfile
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS base
WORKDIR /app
EXPOSE 80
EXPOSE 443

FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
WORKDIR /src
COPY ["TodoApi/TodoApi.csproj", "TodoApi/"]
RUN dotnet restore "TodoApi/TodoApi.csproj"
COPY . .
WORKDIR "/src/TodoApi"
RUN dotnet build "TodoApi.csproj" -c Release -o /app/build

FROM build AS publish
RUN dotnet publish "TodoApi.csproj" -c Release -o /app/publish

FROM base AS final
WORKDIR /app
COPY --from=publish /app/publish .
ENTRYPOINT ["dotnet", "TodoApi.dll"]
# docker-compose.yml
version: '3.8'

services:
  todoapi:
    build: .
    ports:
      - "8080:80"
    environment:
      - ASPNETCORE_ENVIRONMENT=Production
      - ConnectionStrings__DefaultConnection=Server=db;Database=TodoDB;User=sa;Password=YourPassword123;
    depends_on:
      - db

  db:
    image: mcr.microsoft.com/mssql/server:2022-latest
    environment:
      - ACCEPT_EULA=Y
      - SA_PASSWORD=YourPassword123
    ports:
      - "1433:1433"
    volumes:
      - sqldata:/var/opt/mssql

volumes:
  sqldata:

13.4 监控和诊断

Application Insights集成

// 添加Application Insights
builder.Services.AddApplicationInsightsTelemetry();

// 自定义遥测
public class TodoItemsController : ControllerBase
{
    private readonly TelemetryClient _telemetryClient;

    public TodoItemsController(TelemetryClient telemetryClient)
    {
        _telemetryClient = telemetryClient;
    }

    [HttpPost]
    public async Task<ActionResult<TodoItem>> CreateTodoItem(TodoItem item)
    {
        _telemetryClient.TrackEvent("TodoItemCreated", 
            new Dictionary<string, string> { { "ItemName", item.Name } });

        // 业务逻辑...
    }
}

性能计数器

// 自定义性能指标
public class MetricsService
{
    private readonly IMetrics _metrics;
    private readonly Counter<int> _todoItemsCreated;

    public MetricsService(IMeterFactory meterFactory)
    {
        var meter = meterFactory.Create("TodoApi");
        _todoItemsCreated = meter.CreateCounter<int>("todo_items_created");
    }

    public void IncrementTodoItemsCreated()
    {
        _todoItemsCreated.Add(1);
    }
}

总结

学习成果

通过本手册的学习,您应该能够:

  1. 理解.NET Web API的基本概念和应用场景
  2. 搭建完整的开发环境并创建第一个Web API项目
  3. 掌握控制器和动作方法的开发技巧
  4. 设计RESTful的路由系统
  5. 实现模型绑定和数据验证
  6. 应用依赖注入和服务配置最佳实践
  7. 创建和使用中间件构建请求管道
  8. 集成Entity Framework Core进行数据访问
  9. 实现身份验证和授权机制
  10. 处理异常和记录日志
  11. 生成API文档并使用Swagger
  12. 编写单元测试和集成测试
  13. 部署应用程序到生产环境并进行性能优化

后续学习建议

  1. 深入学习架构模式:研究Clean Architecture、DDD、CQRS等高级架构模式
  2. 微服务开发:了解如何将单体应用拆分为微服务
  3. API网关:学习使用Ocelot、YARP等API网关技术
  4. 消息队列:集成RabbitMQ、Azure Service Bus等消息系统
  5. 缓存策略:深入学习Redis、分布式缓存等技术
  6. 容器化:掌握Docker和Kubernetes的使用
  7. 云原生开发:学习云平台特定的服务和最佳实践

最佳实践总结

  1. 遵循REST原则设计API
  2. 使用异步编程提高性能
  3. 实施适当的错误处理和日志记录
  4. 编写全面的测试确保代码质量
  5. 应用安全最佳实践保护API
  6. 使用版本控制管理API变更
  7. 监控和度量应用程序性能
  8. 持续学习保持技术更新