问题描述
目前它正在返回一个单一的难以理解的块,由多个这样的东西粘在一起,像这样:
在 ProyectX.Services.Service.Validate(IList 1 myParam) 中 C:\\Repositories\\projectx\\src\\ProyectX.Services\\Service.cs:line 116\r\n 在 ProyectX.Services.Service.Validate(IList 1 myParam) 中 C:\\Repositories\\projectx\\src\\ProyectX.Services\\Service.cs:line 116\r\n
目标:
在 C:\Repositories\projectx\src\ProyectX.Services\Service.cs:line 116 中的 ProyectX.Services.Service.Validate(IList 1 myParam)
在 C:\Repositories\projectx\src\ProyectX.Services\Service.cs:line 116 中的 ProyectX.Services.Service.Validate(IList 1 myParam)
我试过
Regex.Unescape(exception.StackTrace)
JsonSerializer.Serialize(exception.StackTrace,new JsonSerializerOptions() {WriteIndented = true });
中间件在 Startup.cs 中
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
app.UseMiddleware<ErrorHandlerMiddleware>();
中间件:
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.Json;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Hosting;
using ProyectX.Domain.Exceptions;
using ProyectX.Domain.Models;
namespace ProyectX.API.Middleware
{
public class ErrorHandlerMiddleware
{
private readonly RequestDelegate _next;
public ErrorHandlerMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context,IWebHostEnvironment env)
{
try
{
await _next(context);
}
catch (Exception exception)
{
var response = context.Response;
response.ContentType = "application/json";
switch (exception)
{
case InvalidOperationException:
response.StatusCode = (int)HttpStatusCode.BadRequest;
break;
default:
response.StatusCode = (int)HttpStatusCode.InternalServerError;
break;
}
var details = new Dictionary<string,string>
{
{ "Message",exception.Message },};
if (env.IsDevelopment())
{
details.Add("StackTrace",exception.StackTrace);
}
var errorResponse = new ErrorResponseModel(exception,details);
var result = JsonSerializer.Serialize(errorResponse);
await response.WriteAsync(result);
}
}
}
}
错误响应模型
using System;
using System.Collections.Generic;
namespace ProyectX.Domain.Models
{
public class ErrorResponseModel
{
public ErrorResponseModel(Exception ex,Dictionary<string,string> details)
{
Type = ex.GetType().Name;
Details = details;
}
public string Type { get; set; }
public IDictionary<string,string> Details { get; set; }
}
}
解决方法
我建议在您的 ErrorResponseModel
类中进行一些更改,以便您的字典接受对象类型作为值,例如 Dictionary<string,object>
。这里的想法是,您可以添加字符串或字符串数组。
因此,对于您的跟踪跟踪,您可以使用 split by "\r\n" 将其拆分为多行并获取它的字符串数组,我可以将其传递给我的 StackTrace 值。
让我们来看看你的模型:
public class ErrorResponseModel
{
public ErrorResponseModel(Exception ex,Dictionary<string,object> details)
{
Type = ex.GetType().Name;
Details = details;
}
public string Type { get; set; }
public IDictionary<string,object> Details { get; set; }
}
这里是处理异常的部分:
var details = new Dictionary<string,object>
{
{ "Message",exception.Message},};
var lines = exception.StackTrace?.Split("\r\n").Select(e => e.TrimStart());
details.Add("StackTrace",lines);
var errorResponse = new ErrorResponseModel(exception,details);
var result = JsonSerializer.Serialize(errorResponse,new JsonSerializerOptions() { WriteIndented = true });
所有这些返回如下:
,使用 StackTrace 和 StackFrame 类的异常信息模型
有一个 StackTrace 类可以帮助您获取 StackFrame 对象的列表。每个帧,包括如下信息:
- 行号
- 方法(类、程序集)
- 代码文件
查看 ASP.NET Core ErrorPage 和 DeveloperExceptionPageMiddleware,您将看到该框架创建了一个模型类,包括堆栈帧等异常信息,然后在错误页面中格式化了模型并显示在一种可读的格式。
这就是 Exception 类生成 StackTrace
的方式:通过调用 StackTrace.ToString 获取堆栈帧和字符串格式的上述信息。
您也可以这样做并以更好的格式生成字符串,或者作为一个更好的主意,创建一个异常或堆栈跟踪模型并让格式设置在以后完成。
例如,这是您可以拥有的异常模型(包括堆栈跟踪):
{
"Message":"Attempted to divide by zero.","StackFrames":[
{
"LineNumber":13,"Method":"Main(String[] args)","Class":"SampleConsoleApp.Program","AssemblyName":"SampleConsoleApp,Version=1.0.0.0,Culture=neutral,PublicKeyToken=null","AssemblyFile":"file:///C:/SampleConsoleApp/bin/Debug/netcoreapp3.1/SampleConsoleApp.dll","CodeFile":"C:\\SampleConsoleApp\\Program.cs"
},{
"LineNumber":23,"Method":"Divide(Int32 x,Int32 y)","CodeFile":"C:\\SampleConsoleApp\\Program.cs"
}
],"InnerException":null
}
上面的 json 字符串是从我的异常模型中创建的:
try
{
}
catch (Exception ex)
{
var model = ExceptionModel.Create(ex);
// You can convert it to json and store it
// Or format it and write to log
}
ExceptionModel 包括 StackTrace
以下是我实现异常模型的方式:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
public class ExceptionModel
{
public string Message { get; set; }
public IEnumerable<StackFrameModel> StackFrames { get; set; }
public ExceptionModel InnerException { get; set; }
public static ExceptionModel Create(Exception ex = null)
{
var trace = ex == null ? new StackTrace(true) : new StackTrace(ex,true);
var model = new ExceptionModel();
model.Message = ex?.Message;
model.StackFrames = trace.GetFrames().Reverse()
.Select(x => new StackFrameModel()
{
LineNumber = x.GetFileLineNumber(),Method = GetMethodSignature(x.GetMethod()),Class = x.GetMethod()?.DeclaringType?.FullName,AssemblyName = x.GetMethod()?.DeclaringType?.Assembly?.FullName,AssemblyFile = x.GetMethod()?.DeclaringType?.Assembly?.CodeBase,CodeFile = x.GetFileName(),});
if (ex?.InnerException != null)
model.InnerException = ExceptionModel.Create(ex.InnerException);
return model;
}
private static string GetTypeName(Type type)
{
return type?.FullName?.Replace('+','.');
}
private static string GetMethodSignature(MethodBase mb)
{
var sb = new StringBuilder();
sb.Append(mb.Name);
// deal with the generic portion of the method
if (mb is MethodInfo && ((MethodInfo)mb).IsGenericMethod)
{
Type[] typars = ((MethodInfo)mb).GetGenericArguments();
sb.Append("[");
int k = 0;
bool fFirstTyParam = true;
while (k < typars.Length)
{
if (fFirstTyParam == false)
sb.Append(",");
else
fFirstTyParam = false;
sb.Append(typars[k].Name);
k++;
}
sb.Append("]");
}
// arguments printing
sb.Append("(");
ParameterInfo[] pi = mb.GetParameters();
bool fFirstParam = true;
for (int j = 0; j < pi.Length; j++)
{
if (fFirstParam == false)
sb.Append(",");
else
fFirstParam = false;
String typeName = "<UnknownType>";
if (pi[j].ParameterType != null)
typeName = pi[j].ParameterType.Name;
sb.Append(typeName + " " + pi[j].Name);
}
sb.Append(")");
return sb.ToString();
}
}
public class StackFrameModel
{
public int LineNumber { get; set; }
public string Method { get; set; }
public string Class { get; set; }
public string AssemblyName { get; set; }
public string AssemblyFile { get; set; }
public string CodeFile { get; set; }
}