feat: 實作完整錯誤日誌系統
- 新增 ErrorHandlingMiddleware 全域錯誤處理 - 升級日誌配置到 Debug 層級 - 添加詳細的認證流程日誌 - 改進 AuthController 錯誤處理 - 包含請求詳情和錯誤堆疊追蹤 - 支援結構化日誌記錄 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
c94cf75838
commit
e5cb336667
|
|
@ -35,9 +35,17 @@ public class AuthController : ControllerBase
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Registration attempt for user: {Username}, Email: {Email}",
|
||||||
|
request.Username, request.Email);
|
||||||
|
|
||||||
// 驗證請求
|
// 驗證請求
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invalid model state for registration: {@ModelErrors}",
|
||||||
|
ModelState.Where(x => x.Value?.Errors.Count > 0)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value?.Errors.Select(e => e.ErrorMessage)));
|
||||||
return BadRequest(new { Success = false, Error = "Invalid request data" });
|
return BadRequest(new { Success = false, Error = "Invalid request data" });
|
||||||
|
}
|
||||||
|
|
||||||
// 檢查Email是否已存在
|
// 檢查Email是否已存在
|
||||||
if (await _context.Users.AnyAsync(u => u.Email == request.Email))
|
if (await _context.Users.AnyAsync(u => u.Email == request.Email))
|
||||||
|
|
@ -90,11 +98,17 @@ public class AuthController : ControllerBase
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error during user registration");
|
_logger.LogError(ex, "Error during user registration. Request: {@Request}", new {
|
||||||
|
Username = request.Username,
|
||||||
|
Email = request.Email,
|
||||||
|
StackTrace = ex.StackTrace,
|
||||||
|
InnerException = ex.InnerException?.Message
|
||||||
|
});
|
||||||
return StatusCode(500, new
|
return StatusCode(500, new
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false,
|
||||||
Error = "Registration failed",
|
Error = "Registration failed",
|
||||||
|
Details = ex.Message,
|
||||||
Timestamp = DateTime.UtcNow
|
Timestamp = DateTime.UtcNow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -105,9 +119,16 @@ public class AuthController : ControllerBase
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
_logger.LogInformation("Login attempt for email: {Email}", request.Email);
|
||||||
|
|
||||||
// 驗證請求
|
// 驗證請求
|
||||||
if (!ModelState.IsValid)
|
if (!ModelState.IsValid)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("Invalid model state for login: {@ModelErrors}",
|
||||||
|
ModelState.Where(x => x.Value?.Errors.Count > 0)
|
||||||
|
.ToDictionary(x => x.Key, x => x.Value?.Errors.Select(e => e.ErrorMessage)));
|
||||||
return BadRequest(new { Success = false, Error = "Invalid request data" });
|
return BadRequest(new { Success = false, Error = "Invalid request data" });
|
||||||
|
}
|
||||||
|
|
||||||
// 查找用戶
|
// 查找用戶
|
||||||
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.Email);
|
var user = await _context.Users.FirstOrDefaultAsync(u => u.Email == request.Email);
|
||||||
|
|
@ -143,11 +164,16 @@ public class AuthController : ControllerBase
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "Error during user login");
|
_logger.LogError(ex, "Error during user login. Request: {@Request}", new {
|
||||||
|
Email = request.Email,
|
||||||
|
StackTrace = ex.StackTrace,
|
||||||
|
InnerException = ex.InnerException?.Message
|
||||||
|
});
|
||||||
return StatusCode(500, new
|
return StatusCode(500, new
|
||||||
{
|
{
|
||||||
Success = false,
|
Success = false,
|
||||||
Error = "Login failed",
|
Error = "Login failed",
|
||||||
|
Details = ex.Message,
|
||||||
Timestamp = DateTime.UtcNow
|
Timestamp = DateTime.UtcNow
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
using System.Net;
|
||||||
|
using System.Text.Json;
|
||||||
|
|
||||||
|
namespace DramaLing.Api.Middleware;
|
||||||
|
|
||||||
|
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 InvokeAsync(HttpContext context)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _next(context);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An unhandled exception occurred during request execution. Request: {Method} {Path}",
|
||||||
|
context.Request.Method, context.Request.Path);
|
||||||
|
|
||||||
|
await HandleExceptionAsync(context, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleExceptionAsync(HttpContext context, Exception exception)
|
||||||
|
{
|
||||||
|
context.Response.ContentType = "application/json";
|
||||||
|
|
||||||
|
var errorResponse = new
|
||||||
|
{
|
||||||
|
Success = false,
|
||||||
|
Error = "An error occurred while processing your request",
|
||||||
|
Details = exception.Message,
|
||||||
|
Timestamp = DateTime.UtcNow,
|
||||||
|
RequestId = context.TraceIdentifier
|
||||||
|
};
|
||||||
|
|
||||||
|
var statusCode = exception switch
|
||||||
|
{
|
||||||
|
ArgumentException => HttpStatusCode.BadRequest,
|
||||||
|
UnauthorizedAccessException => HttpStatusCode.Unauthorized,
|
||||||
|
KeyNotFoundException => HttpStatusCode.NotFound,
|
||||||
|
NotImplementedException => HttpStatusCode.NotImplemented,
|
||||||
|
TimeoutException => HttpStatusCode.RequestTimeout,
|
||||||
|
_ => HttpStatusCode.InternalServerError
|
||||||
|
};
|
||||||
|
|
||||||
|
context.Response.StatusCode = (int)statusCode;
|
||||||
|
|
||||||
|
var jsonResponse = JsonSerializer.Serialize(errorResponse, new JsonSerializerOptions
|
||||||
|
{
|
||||||
|
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
|
||||||
|
});
|
||||||
|
|
||||||
|
_logger.LogError("Error response sent: {StatusCode} - {Response}", statusCode, jsonResponse);
|
||||||
|
|
||||||
|
await context.Response.WriteAsync(jsonResponse);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using DramaLing.Api.Data;
|
using DramaLing.Api.Data;
|
||||||
using DramaLing.Api.Services;
|
using DramaLing.Api.Services;
|
||||||
|
using DramaLing.Api.Middleware;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
@ -111,6 +112,10 @@ builder.Services.AddSwaggerGen(c =>
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
|
|
||||||
|
// 全域錯誤處理中介軟體 (必須放在最前面)
|
||||||
|
app.UseMiddleware<ErrorHandlingMiddleware>();
|
||||||
|
|
||||||
if (app.Environment.IsDevelopment())
|
if (app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,18 @@
|
||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Debug",
|
||||||
"Microsoft.AspNetCore": "Warning",
|
"Microsoft.AspNetCore": "Information",
|
||||||
"Microsoft.EntityFrameworkCore": "Warning"
|
"Microsoft.EntityFrameworkCore": "Information",
|
||||||
|
"DramaLing.Api": "Debug",
|
||||||
|
"System": "Information",
|
||||||
|
"Microsoft": "Information"
|
||||||
|
},
|
||||||
|
"Console": {
|
||||||
|
"IncludeScopes": true,
|
||||||
|
"LogLevel": {
|
||||||
|
"Default": "Debug"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AllowedHosts": "*",
|
"AllowedHosts": "*",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue