docs: 完善身份模块文档注释与字段说明
This commit is contained in:
@@ -12,20 +12,29 @@ namespace TakeoutSaaS.Shared.Web.Filters;
|
||||
/// </summary>
|
||||
public sealed class ApiResponseResultFilter : IAsyncResultFilter
|
||||
{
|
||||
/// <summary>
|
||||
/// 执行结果过滤,将 ApiResponse 映射为对应 HTTP 状态码。
|
||||
/// </summary>
|
||||
/// <param name="context">结果执行上下文。</param>
|
||||
/// <param name="next">后续委托。</param>
|
||||
/// <returns>异步任务。</returns>
|
||||
public Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
|
||||
{
|
||||
// 1. 仅处理 ObjectResult
|
||||
// 只处理 ObjectResult 类型的结果
|
||||
if (context.Result is not ObjectResult objectResult)
|
||||
{
|
||||
return next();
|
||||
}
|
||||
|
||||
// 2. 结果为空直接跳过
|
||||
var value = objectResult.Value;
|
||||
if (value == null)
|
||||
{
|
||||
return next();
|
||||
}
|
||||
|
||||
// 3. 确认类型为 ApiResponse<T>
|
||||
// 检查是否是 ApiResponse<T> 类型
|
||||
var valueType = value.GetType();
|
||||
if (!IsApiResponseType(valueType))
|
||||
@@ -33,6 +42,7 @@ public sealed class ApiResponseResultFilter : IAsyncResultFilter
|
||||
return next();
|
||||
}
|
||||
|
||||
// 4. 读取 Success 与 Code
|
||||
// 使用反射获取 Success 和 Code 属性
|
||||
// 注意:由于已通过 IsApiResponseType 检查,属性名是固定的
|
||||
const string successPropertyName = "Success";
|
||||
@@ -48,9 +58,11 @@ public sealed class ApiResponseResultFilter : IAsyncResultFilter
|
||||
var success = (bool)(successProperty.GetValue(value) ?? false);
|
||||
var code = (int)(codeProperty.GetValue(value) ?? 200);
|
||||
|
||||
// 5. 映射 HTTP 状态码
|
||||
// 根据 Success 和 Code 设置 HTTP 状态码
|
||||
var statusCode = success ? MapSuccessCode(code) : MapErrorCode(code);
|
||||
|
||||
// 6. 回写状态码
|
||||
// 更新 ObjectResult 的状态码
|
||||
objectResult.StatusCode = statusCode;
|
||||
|
||||
@@ -101,4 +113,3 @@ public sealed class ApiResponseResultFilter : IAsyncResultFilter
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,8 +10,13 @@ namespace TakeoutSaaS.Shared.Web.Filters;
|
||||
/// </summary>
|
||||
public sealed class ValidateModelAttribute : ActionFilterAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// 在 Action 执行前拦截模型验证错误。
|
||||
/// </summary>
|
||||
/// <param name="context">执行上下文。</param>
|
||||
public override void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
// 1. 模型验证未通过则返回 422
|
||||
if (!context.ModelState.IsValid)
|
||||
{
|
||||
var errors = context.ModelState
|
||||
|
||||
@@ -15,8 +15,13 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<Correl
|
||||
private const string SpanHeader = "X-Span-Id";
|
||||
private const string RequestHeader = "X-Request-Id";
|
||||
|
||||
/// <summary>
|
||||
/// 管道入口,确保 TraceId/SpanId 贯穿请求。
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP 上下文。</param>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 1. 确保活动存在并启动
|
||||
var ownsActivity = Activity.Current is null;
|
||||
var activity = Activity.Current ?? new Activity("TakeoutSaaS.Request");
|
||||
|
||||
@@ -26,6 +31,7 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<Correl
|
||||
activity.Start();
|
||||
}
|
||||
|
||||
// 2. 生成/解析 TraceId、SpanId
|
||||
var traceId = activity.TraceId.ToString();
|
||||
var spanId = activity.SpanId.ToString();
|
||||
|
||||
@@ -34,6 +40,7 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<Correl
|
||||
traceId = ResolveTraceId(context);
|
||||
}
|
||||
|
||||
// 3. 写入上下文与响应头
|
||||
context.TraceIdentifier = traceId;
|
||||
TraceContext.TraceId = traceId;
|
||||
TraceContext.SpanId = spanId;
|
||||
@@ -45,6 +52,7 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<Correl
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
|
||||
// 4. 带 Scope 调用后续中间件
|
||||
using (logger.BeginScope(new Dictionary<string, object>
|
||||
{
|
||||
["TraceId"] = traceId,
|
||||
@@ -57,6 +65,7 @@ public sealed class CorrelationIdMiddleware(RequestDelegate next, ILogger<Correl
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 5. 清理上下文与活动
|
||||
TraceContext.Clear();
|
||||
if (ownsActivity)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,10 @@ public sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<Ex
|
||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// 中间件入口,捕获并统一处理异常。
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP 上下文。</param>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
try
|
||||
@@ -39,17 +43,21 @@ public sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<Ex
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 1. 记录异常
|
||||
logger.LogError(ex, "未处理异常:{Message}", ex.Message);
|
||||
// 2. 返回统一错误响应
|
||||
await HandleExceptionAsync(context, ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||
{
|
||||
// 1. 构建错误响应与状态码
|
||||
var (statusCode, response) = BuildErrorResponse(exception);
|
||||
|
||||
if (environment.IsDevelopment())
|
||||
{
|
||||
// 2. 开发环境附加细节
|
||||
response = response with
|
||||
{
|
||||
Message = exception.Message,
|
||||
@@ -61,6 +69,7 @@ public sealed class ExceptionHandlingMiddleware(RequestDelegate next, ILogger<Ex
|
||||
};
|
||||
}
|
||||
|
||||
// 3. 写入响应
|
||||
context.Response.StatusCode = statusCode;
|
||||
context.Response.ContentType = "application/json";
|
||||
return context.Response.WriteAsJsonAsync(response, SerializerOptions);
|
||||
|
||||
@@ -10,8 +10,13 @@ namespace TakeoutSaaS.Shared.Web.Middleware;
|
||||
/// </summary>
|
||||
public sealed class RequestLoggingMiddleware(RequestDelegate next, ILogger<RequestLoggingMiddleware> logger)
|
||||
{
|
||||
/// <summary>
|
||||
/// 记录请求日志并调用后续中间件。
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP 上下文。</param>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 1. 启动计时
|
||||
var stopwatch = Stopwatch.StartNew();
|
||||
try
|
||||
{
|
||||
@@ -19,6 +24,7 @@ public sealed class RequestLoggingMiddleware(RequestDelegate next, ILogger<Reque
|
||||
}
|
||||
finally
|
||||
{
|
||||
// 2. 结束计时并输出日志
|
||||
stopwatch.Stop();
|
||||
var traceId = TraceContext.TraceId ?? context.TraceIdentifier;
|
||||
var spanId = TraceContext.SpanId ?? Activity.Current?.SpanId.ToString() ?? string.Empty;
|
||||
|
||||
@@ -7,14 +7,19 @@ namespace TakeoutSaaS.Shared.Web.Middleware;
|
||||
/// </summary>
|
||||
public sealed class SecurityHeadersMiddleware(RequestDelegate next)
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置基础安全响应头。
|
||||
/// </summary>
|
||||
/// <param name="context">HTTP 上下文。</param>
|
||||
public async Task InvokeAsync(HttpContext context)
|
||||
{
|
||||
// 1. 写入安全响应头
|
||||
var headers = context.Response.Headers;
|
||||
headers["X-Content-Type-Options"] = "nosniff";
|
||||
headers["X-Frame-Options"] = "DENY";
|
||||
headers["X-XSS-Protection"] = "1; mode=block";
|
||||
headers["Referrer-Policy"] = "no-referrer";
|
||||
// 2. 继续后续管道
|
||||
await next(context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user