我正在使用.Net Core 2.1创建ASP.Net Core Web API,并且需要创建用于全局异常处理的自定义中间件。我正在尝试做的是在应用程序中发生异常时捕获JSON请求。由于我使用的是自定义中间件,因此我希望在中间件中可以使用JSON请求。我怎样才能做到这一点?
我尝试通过遵循Marinko Spasojevic上的一篇文章来创建用于集中式异常处理的自定义中间件,并对其进行了少许修改以捕获json请求。似乎该请求已不可用,因为该异常发生在控制器操作内(不在中间件中)。这是我的代码:
这是我的错误日志模型
public class ErrorLog
{
public DateTime LogDate { get; set; }
public string URL { get; set; }
public string Request { get; set; }
public string Source { get; set; }
public string Message { get; set; }
}
这是我的项目中使用的标准响应模型
public class BaseResponse<T> : IBaseResponse where T : class
{
public bool Status { get; set; }
public string Message { get; set; }
public IEnumerable<T> Data { get; set; }
}
这是我的自定义异常中间件
public class GlobalException
{
private readonly RequestDelegate _next;
private readonly ICustomLogger _logger;
public GlobalException(RequestDelegate next, ICustomLogger logger)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext httpContext)
{
try
{
await _next(httpContext);
}
catch (Exception ex)
{
ErrorLog log = new ErrorLog();
log = await HandleLogError(httpContext, ex);
_logger.LogError(log); // Custom build logger
await HandleExceptionAsync(httpContext, ex);
}
}
private static async Task HandleExceptionAsync(HttpContext context, Exception exception)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
BaseResponse<object> response = new BaseResponse<object>();
response.Status = false;
response.Message = "There is an exception occured.";
response.Data = new List<object>();
await context.Response.WriteAsync(response.Serialize());
}
private static async Task<ErrorLog> HandleLogError(HttpContext context, Exception exception)
{
Stream body = context.Request.Body;
context.Request.EnableRewind();
byte[] buffer = new byte[Convert.ToInt32(context.Request.ContentLength)];
await context.Request.Body.ReadAsync(buffer, 0, buffer.Length);
string requestText = Encoding.UTF8.GetString(buffer);
context.Request.Body = body;
ErrorLog log = new ErrorLog();
UriBuilder builder = new UriBuilder();
builder.Scheme = context.Request.Scheme;
builder.Host = context.Request.Host.Host;
builder.Path = context.Request.Path.ToString();
builder.Query = context.Request.QueryString.ToString();
log.LogDate = DateTime.Now;
log.URL = builder.Uri.ToString();
log.Request = requestText;
log.Source = exception.Source;
log.Message = exception.Message;
return log;
}
}
最后注册中间件
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseMiddleware<GlobalException>();
...
}
那么,任何人都可以给我启示吗?任何帮助将不胜感激,谢谢您。
答案 0 :(得分:3)
似乎该请求已不可用,因为该异常发生在控制器操作内(而不是在中间件中)。
首先,如果您要获取的是错误信息,则可以通过ex.InnerException
得到根错误。
我想做的是在应用程序中发生异常时捕获JSON请求。
此外,您可以在错误处理中间件中读取和记录请求和响应(在这种情况下,我假设它已序列化为json),如下所示。
public async Task InvokeAsync(HttpContext context)
{
var requestAsString = await FormatRequest(context.Request);
var originalBodyStream = context.Response.Body;
using (var responseBody = new MemoryStream())
{
context.Response.Body = responseBody;
await _next(context);
var responseString = await FormatResponse(context.Response);
await responseBody.CopyToAsync(originalBodyStream);
}
}
private async Task<string> FormatRequest(HttpRequest request)
{
var body = request.Body;
request.EnableRewind();
var buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
var bodyAsText = Encoding.UTF8.GetString(buffer);
request.Body = body;
return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
}
private async Task<string> FormatResponse(HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
var text = await new StreamReader(response.Body).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Response {text}";
}
顺便说一句,我做了一些小的改动以使其正确地适合您的问题,但是功劳已转到此gist页。希望这能解决您的问题。
答案 1 :(得分:0)
显然,导致请求无法检索的这部分代码
...
try
{
await _next(httpContext); // this will change the httpContext contents
}
...
这是我的最终代码
public class GlobalException
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
public GlobalException(RequestDelegate next, ILogger logger)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
HttpContext tempCtx = context; // had to contain the http context
string request = await FormatRequest(context.Request);
try
{
await _next(context);
}
catch (Exception ex)
{
ErrorLog log = new ErrorLog();
UriBuilder builder = new UriBuilder();
builder.Scheme = tempCtx.Request.Scheme;
builder.Host = tempCtx.Request.Host.Host;
builder.Path = tempCtx.Request.Path.ToString();
builder.Query = tempCtx.Request.QueryString.ToString();
log.LogDate = DateTime.Now;
log.URL = builder.Uri.ToString();
log.Request = request;
log.Source = ex.Source;
log.Message = ex.Message;
await _logger.LogError(log); // custom logger
await HandleExceptionAsync(context);
}
}
private async Task<string> FormatRequest(HttpRequest request)
{
request.EnableRewind();
var body = request.Body;
byte[] buffer = new byte[Convert.ToInt32(request.ContentLength)];
await request.Body.ReadAsync(buffer, 0, buffer.Length);
string requestBody = Encoding.UTF8.GetString(buffer);
body.Seek(0, SeekOrigin.Begin);
request.Body = body;
return requestBody;
}
private async Task HandleExceptionAsync(HttpContext context)
{
context.Response.ContentType = "application/json";
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
BaseResponse<object> response = new BaseResponse<object>();
response.Status = false;
response.Message = "There is an exception occured.";
response.Data = new List<object>();
await context.Response.WriteAsync(response.Serialize());
}
}
非常感谢Hasan给我的启发。