Files
ASP.NET-CORE-web-test/Program.cs
2025-10-10 16:02:38 +08:00

289 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using FluentValidation; // <--- 关键!这是现在唯一需要的 using
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using SimpleTodoApiWithPg.Data;
using SimpleTodoApiWithPg.Endpoints;
using SimpleTodoApiWithPg.UserService;
using System.Security.Cryptography.X509Certificates;
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddConsole(); //日志服务
builder.Services.AddScoped<UserServices>(); //注册用户服务
// 1. 添加OpenAPI/Swagger生成器服务到容器
builder.Services.AddEndpointsApiExplorer(); // 这个服务负责发现和描述API端点
builder.Services.AddSwaggerGen(); // 这个服务负责根据端点描述生成OpenAPI JSON文档
// 1. 扫描并注册你项目里的所有验证器
builder.Services.AddValidatorsFromAssemblyContaining<Program>(includeInternalTypes: true);
// 注册所有 validator按包含类型或指定程序集
var certPath = builder.Configuration["Jwt:CertificatePath"];
var certPassword = builder.Configuration["Jwt:CertificatePassword"];
var certificate = X509CertificateLoader.LoadPkcs12FromFile(certPath!, certPassword);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = 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 ECDsaSecurityKey(certificate.GetECDsaPublicKey()!),
// 确保指定正确的签名算法
// 对于 ECDSA P-256应该是 "ES256"
ValidAlgorithms = [SecurityAlgorithms.EcdsaSha256] // 可选,增加安全性
};
// 新增:配置认证事件
options.Events = new JwtBearerEvents
{
// 当认证失败需要向客户端发起质询Challenge时触发
OnChallenge = context =>
{
// 阻止默认的响应行为,我们将来完全自定义响应
context.HandleResponse();
context.Response.StatusCode = 401;
context.Response.ContentType = "application/json";
// 默认的错误信息
var message = "身份验证失败,请检查您的 Token。";
// 检查具体的失败原因,如果是 Token 过期,就给出更明确的提示
// AuthenticateFailure 属性包含了认证失败时的异常信息
if (context.AuthenticateFailure is SecurityTokenExpiredException)
{
message = "您的 Token 已过期,请重新登录或刷新 Token。";
}
// 您也可以在这里记录日志,方便排查问题
// logger.LogError(context.AuthenticateFailure, "An authentication error occurred.");
var response = new
{
success = false,
message = message
};
// 将自定义的响应内容写入 Response Body
return context.Response.WriteAsJsonAsync(response);
}
};
});
builder.Services.AddAuthorization();
// === 配置 CORS ===
// 定义一个 CORS 策略名称
var MyAllowSpecificOrigins = "_myAllowSpecificOrigins";
// 添加 CORS 服务到依赖注入容器
builder.Services.AddCors(options =>
{
options.AddPolicy(name: MyAllowSpecificOrigins,
policy =>
{
// 允许所有来源进行访问。
// 注意:这在生产环境中是极度不安全的,仅用于本地开发和测试。
// 在生产环境中,您应该明确指定允许的源:
// policy.WithOrigins("http://localhost:8080", "https://yourfrontend.com")
// 或者如果您直接打开HTML文件其来源是'null',您可以明确允许它:
// policy.WithOrigins("null") // 在此测试场景下这是一个选项但AllowAnyOrigin更通用
policy.AllowAnyOrigin() // 允许任何来源的请求
.AllowAnyHeader() // 允许任何请求头
.AllowAnyMethod(); // 允许任何 HTTP 方法 (GET, POST, PUT, DELETE)
});
});
// === 配置 EF Core 连接 PostgreSQL ===
// 从配置中获取连接字符串
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
// 添加数据库上下文服务
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseNpgsql(connectionString)); // 使用 Npgsql 作为 PostgreSQL 提供者
var app = builder.Build();
// 2. 在管道中启用中间件 (通常只在开发环境中启用)
if (app.Environment.IsDevelopment())
{
app.UseSwagger(); // 这个中间件负责提供生成的 swagger.json 文件
app.UseSwaggerUI(); // 这个中间件负责提供交互式的Swagger UI界面
}
// === CORS 中间件必须放在其他中间件之前 (例如路由,授权,认证等) ===
// 启用 CORS
app.UseCors(MyAllowSpecificOrigins);
// === 应用数据库迁移 (仅在开发环境中推荐) ===
// 在应用启动时自动应用所有挂起的数据库迁移
using (var scope = app.Services.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
dbContext.Database.Migrate();
}
// === 定义 API 端点 (Endpoints) ===
// GET / - 根路径,返回一个简单的消息
app.MapGet("/", () => "Hello World!");
app.MapUserEndpoints();
app.UseAuthentication();
app.UseAuthorization();
//app.MapPatch("/users", async (User user, UserService userService) =>
//{
// return await userService.PatchUserAsync(user);
//});
//// GET /todos - 获取所有待办事项
//app.MapGet("/todos", async (AppDbContext dbContext) =>
//{
// var todos = await dbContext.Todos.ToListAsync();
// return TypedResults.Ok(todos);
//});
//// GET /users - 获取所有用户
//app.MapGet("/users", async (AppDbContext dbContext) =>
//{
// var users = await dbContext.Users.ToListAsync();
// return TypedResults.Ok(users);
//});
//// GET /todos/{id} - 根据 ID 获取单个待办事项
//app.MapGet("/todos/{id}", async (int id, AppDbContext dbContext) =>
//{
// var todo = await dbContext.Todos.FindAsync(id);
// return todo is not null
// ? TypedResults.Ok(todo)
// : Results.NotFound($"待办事项 (ID: {id}) 不存在。");
//});
// GET /users/{id} - 根据 ID 获取单个用户
//app.MapGet("/users/{id}", async (int id, AppDbContext dbContext) =>
//{
// var user = await dbContext.Users.FindAsync(id);
// return user is not null
// ? TypedResults.Ok(user)
// : Results.NotFound($"用户 (ID: {id}) 不存在。");
//});
//// POST /todos - 新增一个待办事项
//app.MapPost("/todos", async (Todo todo, AppDbContext dbContext) =>
//{
// // EF Core 会自动处理 ID 的生成(如果是自增主键)
// dbContext.Todos.Add(todo);
// await dbContext.SaveChangesAsync(); // 保存更改到数据库
// // 回传 201 Created 并提供新资源的位置
// return Results.Created($"/todos/{todo.Id}", todo);
//});
//// POST /users - 新增一个用户
//app.MapPost("/users", async (User user, AppDbContext dbContext) =>
//{
// user.PasswordHash = RandomNumberGenerator.GetString("0123456789",22);
// Console.WriteLine(user.PasswordHash);
// user.PasswordHash = BCrypt.Net.BCrypt.HashPassword(user.PasswordHash,10,true); // 哈希密码
// // EF Core 会自动处理 ID 的生成(如果是自增主键)
// dbContext.Users.Add(user);
// await dbContext.SaveChangesAsync(); // 保存更改到数据库
// // 回传 201 Created 并提供新资源的位置
// return Results.Created($"/users/{user.Id}", user);
//});
//// PUT /todos/{id} - 更新指定的待办事项
//app.MapPut("/todos/{id}", async (int id, Todo inputTodo, AppDbContext dbContext) =>
//{
// // 查找现有待办事项
// var existingTodo = await dbContext.Todos.FindAsync(id);
// if (existingTodo is null)
// {
// return Results.NotFound($"待办事项 (ID: {id}) 不存在。");
// }
// // 更新属性
// existingTodo.Title = inputTodo.Title;
// existingTodo.IsCompleted = inputTodo.IsCompleted;
// await dbContext.SaveChangesAsync(); // 保存更改到数据库
// return Results.NoContent(); // 回传 204 No Content 表示成功但无内容回传
//});
//// PUT /users/{id} - 更新指定的用户
//app.MapPut("/users/{id}", async (int id, User inputUser, AppDbContext dbContext) =>
//{
// // 查找现有用户
// var existingUser = await dbContext.Users.FindAsync(id);
// if (existingUser is null)
// {
// return Results.NotFound($"用户 (ID: {id}) 不存在。");
// }
// // 更新属性
// existingUser.Username = inputUser.Username;
// existingUser.Email = inputUser.Email;
// existingUser.PasswordHash = BCrypt.Net.BCrypt.HashPassword(inputUser.PasswordHash,10,true); // 哈希密码
// await dbContext.SaveChangesAsync();
// return Results.NoContent();
//});
//// DELETE /todos/{id} - 删除指定的待办事项
//app.MapDelete("/todos/{id}", async (int id, AppDbContext dbContext) =>
//{
// var todoToDelete = await dbContext.Todos.FindAsync(id);
// if (todoToDelete is null)
// {
// return Results.NotFound($"待办事项 (ID: {id}) 不存在。");
// }
// dbContext.Todos.Remove(todoToDelete);
// await dbContext.SaveChangesAsync(); // 保存更改到数据库
// return TypedResults.Ok($"待办事项 (ID: {id}) 已成功删除。");
//});
//// DELETE /users/{id} - 删除指定的用户
//app.MapDelete("/users/{id}", async (int id, AppDbContext dbContext) =>
//{
// var userToDelete = await dbContext.Users.FindAsync(id);
// if (userToDelete is null) return Results.NotFound("用户不存在");
// {
// dbContext.Users.Remove(userToDelete);
// await dbContext.SaveChangesAsync(); // 保存更改到数据库
// return TypedResults.Ok("用户已成功删除");
// }
//});
// 运行应用程式
app.Run();