第 12 章 高级 Web API 特性

阿里云教程3个月前发布
14 0 0

12.1 身份认证与授权 (JWT)

JWT 认证流程:用户登录→服务器生成 JWT 令牌→客户端存储令牌→后续请求携带令牌→服务器验证令牌。

实战案例:JWT 认证与授权

    1. 安装 NuGet 包:Microsoft.AspNetCore.Authentication.JwtBearer。
    2. 配置 JWT(Program.cs):
var jwtSettings = builder.Configuration.GetSection("JwtSettings").Get<JwtSettings>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(options =>
    {
        options.TokenValidationParameters = new TokenValidationParameters
        {
            ValidateIssuer = true, // 验证发行人
            ValidIssuer = jwtSettings.Issuer,
            ValidateAudience = true, // 验证受众
            ValidAudience = jwtSettings.Audience,
            ValidateLifetime = true, // 验证过期时间
            IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.SecretKey)), // 签名密钥
            ClockSkew = TimeSpan.Zero // 禁用时钟偏差(避免令牌提前失效)
        };
    });

// 注册授权服务
builder.Services.AddAuthorization();
    1. 添加 JWT 配置(appsettings.json):
"JwtSettings": {
  "Issuer": "MyWebApi",
  "Audience": "WebApiClients",
  "SecretKey": "YourSuperSecretKey1234567890!@#$%^&*()", // 生产环境需用长随机密钥
  "ExpiresInMinutes": 60 // 令牌有效期60分钟
}
    1. 登录接口(生成 JWT)
[ApiController]
[Route("api/[controller]")]
public class AuthController : ControllerBase
{
    private readonly JwtSettings _jwtSettings;
    private readonly IUserService _userService;

    public AuthController(IOptions<JwtSettings> jwtSettings, IUserService userService)
    {
        _jwtSettings = jwtSettings.Value;
        _userService = userService;
    }

    [HttpPost("login")]
    public async Task<IActionResult> Login([FromBody] LoginRequest request)
    {
        // 1. 验证用户(实际项目需加密验证密码)
        var user = await _userService.ValidateUserAsync(request.UserName, request.Password);
        if (user == null)
        {
            return Unauthorized(new { Message = "用户名或密码错误" });
        }

        // 2. 生成JWT令牌
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.SecretKey));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        // 3. 设置令牌声明(包含用户ID、角色等信息)
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.Name),
            new Claim(ClaimTypes.Role, user.Role) // 角色声明(用于授权)
        };

        // 4. 创建令牌
        var token = new JwtSecurityToken(
            issuer: _jwtSettings.Issuer,
            audience: _jwtSettings.Audience,
            claims: claims,
            expires: DateTime.Now.AddMinutes(_jwtSettings.ExpiresInMinutes),
            signingCredentials: credentials);

        // 5. 返回令牌
        var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
        return Ok(new { Token = tokenString, ExpiresIn = _jwtSettings.ExpiresInMinutes * 60 });
    }
}

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

    [Required]
    public string Password { get; set; }
}
    1. 添加授权(控制器 / 方法)csharp
// 全局启用认证(Program.cs)
app.UseAuthentication();
app.UseAuthorization();

// 1. 控制器级授权(所有方法需登录)
[Authorize]
[ApiController]
[Route("api/v1/users")]
public class UsersController : ControllerBase { /* ... */ }

// 2. 方法级授权(仅管理员可访问)
[HttpDelete("{id}")]
[Authorize(Roles = "Admin")] // 仅Role=Admin的用户可删除
public async Task<IActionResult> DeleteUser(int id) { /* ... */ }
    1. 测试:用 Postman 先调用/api/auth/login获取 Token,再在请求头添加Authorization: Bearer {Token}访问需要授权的接口。

12.2 缓存策略与响应缓存

  • 响应缓存:通过[ResponseCache]特性缓存 API 响应,减少数据库查询,提升性能。
  • 实战案例:缓存用户列表
[ApiController]
[Route("api/v1/users")]
public class UsersController : ControllerBase
{
    // 缓存60秒,按查询参数page和pageSize区分缓存
    [HttpGet]
    [ResponseCache(Duration = 60, VaryByQueryKeys = new[] { "page", "pageSize" })]
    public async Task<ActionResult<PagedResult<UserDto>>> GetUsers(
        [FromQuery] int page = 1, 
        [FromQuery] int pageSize = 10)
    {
        // 数据库查询逻辑(缓存期间仅执行一次)
        var total = await _dbContext.Users.CountAsync();
        var users = await _dbContext.Users
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(u => new UserDto { Id = u.Id, Name = u.Name, Email = u.Email })
            .ToListAsync();

        return Ok(new PagedResult<UserDto> { Total = total, Page = page, PageSize = pageSize, Data = users });
    }
}

// 配置缓存服务(Program.cs)
builder.Services.AddResponseCaching();
app.UseResponseCaching(); // 注册响应缓存中间件

12.3 速率限制与 API 防护

速率限制:防止恶意请求(如 DDOS),限制单位时间内的请求次数,使用AspNetCore.RateLimit包。

实战案例:IP 级速率限制

    1. 安装 NuGet 包:AspNetCore.RateLimit。
    2. 配置速率限制(Program.cs):
// 1. 注册速率限制服务
builder.Services.AddMemoryCache(); // 用内存缓存存储速率限制数据(生产环境可用Redis)
builder.Services.Configure<IpRateLimitOptions>(options =>
{
    options.EnableEndpointRateLimiting = true;
    options.StackBlockedRequests = false;
    options.HttpStatusCode = 429; // 超出限制返回429 Too Many Requests
    options.RealIpHeader = "X-Real-IP";
    options.GeneralRules = new List<RateLimitRule>
    {
        // 规则1:所有接口,1分钟内最多10次请求
        new RateLimitRule
        {
            Endpoint = "*",
            Period = "1m",
            Limit = 10
        },
        // 规则2:登录接口,1分钟内最多5次请求(防止暴力破解)
        new RateLimitRule
        {
            Endpoint = "POST:/api/auth/login",
            Period = "1m",
            Limit = 5
        }
    };
});
builder.Services.AddInMemoryRateLimiting();
builder.Services.AddSingleton<IRateLimitConfiguration, RateLimitConfiguration>();

// 2. 注册速率限制中间件(需在认证前)
app.UseIpRateLimiting();

12.4 文档生成与 Swagger/OpenAPI

  • Swagger:自动生成 API 文档,支持在线调试,使用Swashbuckle.AspNetCore包。
  • 实战案例:配置 Swagger
    1. 安装 NuGet 包:Swashbuckle.AspNetCore。
    2. 配置 Swagger(Program.cs):
builder.Services.AddSwaggerGen(options =>
{
    // 1. 设置文档信息
    options.SwaggerDoc("v1", new OpenApiInfo
    {
        Title = "用户管理API",
        Version = "v1",
        Description = "基于ASP.NET Core的用户管理Web API文档"
    });

    // 2. 启用JWT认证支持(添加Authorize按钮)
    var securityScheme = new OpenApiSecurityScheme
    {
        Name = "Authorization",
        Type = SecuritySchemeType.Http,
        Scheme = "bearer",
        BearerFormat = "JWT",
        In = ParameterLocation.Header,
        Description = "请输入JWT令牌:Bearer {Token}"
    };
    options.AddSecurityDefinition("Bearer", securityScheme);
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                }
            },
            Array.Empty<string>()
        }
    });

    // 3. 加载XML注释(显示方法和模型的注释)
    var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    options.IncludeXmlComments(xmlPath);
});

// 3. 启用Swagger中间件(开发环境)
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI(options =>
    {
        options.SwaggerEndpoint("/swagger/v1/swagger.json", "用户管理API v1");
        options.RoutePrefix = "swagger"; // 文档路径:/swagger
    });
}
    1. 添加 XML 注释:项目属性→生成→勾选 “生成 XML 文档文件”,然后在控制器和模型中添加注释:csharp
/// <summary>
/// 用户管理控制器
/// </summary>
[ApiController]
[Route("api/v1/[controller]")]
public class UsersController : ControllerBase
{
    /// <summary>
    /// 查询单个用户
    /// </summary>
    /// <param>用户ID</param>
    /// <returns>用户信息</returns>
    [HttpGet("{id}")]
    public async Task<ActionResult<UserDto>> GetUserById(int id) { /* ... */ }
}
    1. 访问文档:运行项目后访问/swagger,可查看文档并在线调试 API(点击 “Authorize” 输入 JWT 令牌)。

12.5 gRPC 服务与高性能通信

  • gRPC:基于 HTTP/2 的高性能 RPC 框架,使用 Protocol Buffers(Protobuf)序列化,适合服务间通信。
  • 实战案例:创建 gRPC 服务
    1. 创建 gRPC 服务项目:Visual Studio 新建 “gRPC 服务” 项目,或用 CLI:dotnet new grpc -n UserGrpcService。
    2. 定义 Protobuf 协议(Protos/user.proto):
syntax = "proto3";

package user;

// 定义用户服务
service UserService {
    // 方法1:获取用户信息
    rpc GetUser (GetUserRequest) returns (GetUserResponse);
    // 方法2:批量获取用户
    rpc GetUserList (GetUserListRequest) returns (GetUserListResponse);
}

// 请求模型:获取单个用户
message GetUserRequest {
    int32 user_id = 1; // Protobuf字段编号(不可重复)
}

// 响应模型:单个用户
message GetUserResponse {
    int32 id = 1;
    string name = 2;
    string email = 3;
    int32 age = 4;
}

// 请求模型:批量获取用户
message GetUserListRequest {
    int32 page = 1;
    int32 page_size = 2;
}

// 响应模型:用户列表
message GetUserListResponse {
    repeated GetUserResponse users = 1; // repeated表明数组
    int32 total = 2;
}
    1. 实现 gRPC 服务(Services/UserService.cs):
public class UserService : Protos.UserService.UserServiceBase
{
    private readonly AppDbContext _dbContext;

    public UserService(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    // 实现GetUser方法
    public override async Task<Protos.GetUserResponse> GetUser(
        Protos.GetUserRequest request, 
        ServerCallContext context)
    {
        var user = await _dbContext.Users.FindAsync(request.UserId);
        if (user == null)
        {
            throw new RpcException(new Status(StatusCode.NotFound, "用户不存在"));
        }

        return new Protos.GetUserResponse
        {
            Id = user.Id,
            Name = user.Name,
            Email = user.Email,
            Age = user.Age
        };
    }

    // 实现GetUserList方法
    public override async Task<Protos.GetUserListResponse> GetUserList(
        Protos.GetUserListRequest request, 
        ServerCallContext context)
    {
        var total = await _dbContext.Users.CountAsync();
        var users = await _dbContext.Users
            .Skip((request.Page - 1) * request.PageSize)
            .Take(request.PageSize)
            .ToListAsync();

        var response = new Protos.GetUserListResponse { Total = total };
        foreach (var user in users)
        {
            response.Users.Add(new Protos.GetUserResponse
            {
                Id = user.Id,
                Name = user.Name,
                Email = user.Email,
                Age = user.Age
            });
        }
        return response;
    }
}
    1. 配置 gRPC 服务(Program.cs):
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrpc();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

var app = builder.Build();
app.MapGrpcService<UserService>();
app.MapGet("/", () => "gRPC服务运行中,请使用gRPC客户端调用");
app.Run();
    1. 创建 gRPC 客户端(调用服务)
// 客户端项目安装NuGet包:Grpc.Net.Client、Google.Protobuf、Grpc.Tools
public class GrpcClientDemo
{
    public static async Task CallUserGrpcService()
    {
        // 1. 创建gRPC通道(生产环境用HTTPS)
        var channel = GrpcChannel.ForAddress("http://localhost:5000");
        var client = new Protos.UserService.UserServiceClient(channel);

        // 2. 调用GetUser方法
        try
        {
            var getUserResponse = await client.GetUserAsync(new Protos.GetUserRequest { UserId = 1 });
            Console.WriteLine($"用户信息:ID={getUserResponse.Id}, 姓名={getUserResponse.Name}");
        }
        catch (RpcException ex)
        {
            Console.WriteLine($"调用失败:{ex.Status.Detail}");
        }

        // 3. 调用GetUserList方法
        var getUserListResponse = await client.GetUserListAsync(new Protos.GetUserListRequest
                                                                { Page = 1, PageSize = 10 });
        Console.WriteLine($"总用户数:{getUserListResponse.Total}");
        foreach (var user in getUserListResponse.Users)
        {
            Console.WriteLine($"ID={user.Id}, 姓名={user.Name}");
        }
    }
}
© 版权声明

相关文章

暂无评论

none
暂无评论...