- SacDescriptionAttribute 增加 ShouldSerializeTypeId() 返回 false, 跳过 Attribute.TypeId(System.Type)的序列化,避免 Newtonsoft 抛 "Method may only be called on a Type for which Type.IsGenericParameter is true." (13.0.1 安全版对 Type 成员序列化行为变更后暴露) - ExceptionMiddleware 改用 exception.ToString() 记录完整异常(类型+ 内层 message+堆栈),替代仅取最内层 message,便于定位此类反射异常根因; 该信息仅写入日志,不返回前端
83 lines
3.0 KiB
C#
83 lines
3.0 KiB
C#
using Microsoft.AspNetCore.Http;
|
|
using System.Buffers;
|
|
|
|
namespace YLErp.Web.App
|
|
{
|
|
/// <summary>
|
|
/// 异常处理中间件
|
|
/// </summary>
|
|
internal class ExceptionMiddleware
|
|
{
|
|
private readonly RequestDelegate _next;
|
|
|
|
public ExceptionMiddleware(RequestDelegate next)
|
|
{
|
|
_next = next;
|
|
}
|
|
|
|
public async Task Invoke(HttpContext context)
|
|
{
|
|
try
|
|
{
|
|
await _next.Invoke(context);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
await HandleExceptionMessageAsync(context, ex);
|
|
}
|
|
}
|
|
|
|
private static async Task HandleExceptionMessageAsync(HttpContext context, Exception exception)
|
|
{
|
|
var message = string.Empty;
|
|
|
|
var serviceExpcetion = exception as ServiceException;
|
|
|
|
var request = context.Request;
|
|
|
|
try
|
|
{
|
|
// 暴露完整异常(类型 + 所有内层 message + 堆栈),不再只取最内层 message,
|
|
// 便于定位根因(如 SacInfo 序列化 TypeId 触发的 IsGenericParameter 反射异常)。
|
|
message = exception.ToString();
|
|
|
|
if (serviceExpcetion == null || serviceExpcetion.IsFaultError)
|
|
{
|
|
var result = await request.BodyReader.ReadAsync();
|
|
var reqBody = ConvertBufferToString(result.Buffer);
|
|
LogFactory.GetLogger(context.Request.Path.Value).Error(serviceExpcetion ?? exception, $"[query]:{request.QueryString.Value};[body]:{reqBody}\r\n{message}");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
message += "\r\n\r\n" + ex.Message;
|
|
}
|
|
|
|
if (request.Path.StartsWithSegments("/m/api", StringComparison.OrdinalIgnoreCase) ||
|
|
request.Headers["X-Requested-With"] == "XMLHttpRequest" && !request.Headers["Accept"].Contains("text/html"))
|
|
{
|
|
await context.Response.WriteAsJsonAsync(new
|
|
{
|
|
code = -1,
|
|
success = false,
|
|
msg = message,
|
|
Data = serviceExpcetion?.Tag
|
|
});
|
|
}
|
|
else
|
|
{
|
|
context.Response.ContentType = "text/html";
|
|
var resultHtml = File.ReadAllText(Server.MapPath("Statics/html/ShowError.html"));
|
|
resultHtml = resultHtml.Replace("{{ErrorMsg}}", message);
|
|
await context.Response.WriteAsync(resultHtml);
|
|
}
|
|
}
|
|
|
|
private static string ConvertBufferToString(in ReadOnlySequence<byte> readOnlySequence)
|
|
{
|
|
// Separate method because Span/ReadOnlySpan cannot be used in async methods
|
|
ReadOnlySpan<byte> span = readOnlySequence.IsSingleSegment ? readOnlySequence.First.Span : readOnlySequence.ToArray().AsSpan();
|
|
return System.Text.Encoding.UTF8.GetString(span);
|
|
}
|
|
}
|
|
} |