Я создаю новый веб-сайт API в .NET8, который является последней версией ASP.NET Core.
В моем program.cs я добавил это:
builder.Services.AddExceptionHandler<ErrorHandler>();
app.UseExceptionHandler("/Home/Error");
В случае исключения это действительно относится к моему специальному классу обработки ошибок:
public class ErrorHandler : IExceptionHandler
Я получаю доступ к дополнительным данным, которые мне нужны, следующим образом:
private readonly ILogger<ErrorHandler> logger;
private readonly IHttpContextAccessor _httpContextAccessor;
public ErrorHandler(ILogger<ErrorHandler> logger, IHttpContextAccessor httpContextAccessor)
{
this.logger = logger;
this._httpContextAccessor = httpContextAccessor;
}
Пока все хорошо, но теперь я пытаюсь получить некоторые данные для своего журнала:
private string GetErrorReport(Exception e)
{
StringBuilder sbErrorReport = new StringBuilder(1024);
try
{
HttpResponse response = _httpContextAccessor.HttpContext?.Response;
HttpRequest request = _httpContextAccessor.HttpContext?.Request;
string CurUrl = "";
string CurReferrer = "";
string formData = "";
string clientIP = "";
if (request != null)
{
CurUrl = request.GetEncodedPathAndQuery();
CurReferrer = request.Headers["Referer"].ToString();
if (request.Body != null) //is this the same as request.Form.ToString()?
{
using (StreamReader sr = new StreamReader(request.Body))
{
formData = sr.ReadToEnd();
}
}
if (request.HttpContext != null && request.HttpContext.Connection != null && request.HttpContext.Connection.RemoteIpAddress != null)
{
clientIP = request.HttpContext.Connection.RemoteIpAddress.ToString();
}
}
}
catch (Exception ex)
{
//nothing?
}
return sbErrorReport.ToString();
}
Никто из них, кажется, не работает...
CurUrl выдает «/Home/Error», а не исходный URL-адрес, в котором возникла ошибка. CurReferrer выдает ошибку о том, что заголовки недоступны. formData полностью выходит из строя. clientIP действительно работает, если ::1 подходит для локального хоста.
Есть идеи, как лучше всего записывать ошибки со всеми этими подробностями в файл журнала?
В действии обработчика ошибок вы можете получить подробную информацию об ошибке следующим образом:
var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();
Это позволит получить информацию об ошибке из текущего контекста. затем вы можете использовать exceptionHandlerFeature.Error
, чтобы получить фактическую ошибку. например:
public string? ExceptionMessage { get; set; }
var exceptionHandlerFeature = HttpContext.Features.Get<IExceptionHandlerFeature>();
var error = exceptionHandlerFeature.Error;
Вы также можете прочитать, где произошла ошибка (URL-адрес запроса) из свойства Path
exceptionHandlerFeature
следующим образом:
if (exceptionHandlerFeature.Path == "/")
{
ExceptionMessage ??= string.Empty;
ExceptionMessage += " Page: Home.";
}
HttpContext.Connection.RemoteIpAddress
предоставит вам IP клиента. Фактически, вы можете получить текущего пользователя следующим образом:
if (context.User.Identity is { IsAuthenticated: true })
userName = context.User.Identity.Name;
Обратите внимание, что у вас возникнут проблемы с получением IP-адреса, если ваш сервер расположен за балансировщиком нагрузки или если имеется прокси-сервер.
HttpContext.Request.Form
предоставит вам данные формы.
Request.Headers["Referer"].ToString()
даст вам значение заголовка реферера.
Кстати, вы можете использовать встроенный обработчик для обработки ошибок, примерно так:
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler(exceptionHandlerApp =>
{
exceptionHandlerApp.Run(async context =>
{
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
var errorCode = Guid.NewGuid().ToString();
var exceptionHandlerFeature =
context.Features.Get<IExceptionHandlerFeature>();
// do stuff
context.Response.Redirect("/error?customErrorCode = " + errorCode); // perform 302 redirection with error code in query string
});
});
app.UseHsts(); // you can configure any other non-development features in this if block too, like hsts or https redirection (note that you can do can set up these two in IIS too, although irrelevant)
}
см. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/error-handling?view=aspnetcore-8.0 для получения дополнительной информации об обработке ошибок в ядре asp.net со встроенной ошибкой. промежуточное программное обеспечение обработчика.
возможно попробую await context.Request.ReadFormAsync();
, а возможно await new StreamReader(context.Request.Body).ReadToEndAsync();
StreamReader(context.Request.Body).ReadToEndAsync() должен работать, но это не так.
вы уверены, что в теле запроса действительно что-то есть? проверьте вкладку сети в devtools и проверьте запрос
да, я делаю запрос с почтальоном. в действии данные появляются без проблем
После огромной помощи Эмре Бенера, спасибо вам за это, я понял это. Моя ошибка заключалась в том, что я попытался использовать IHttpContextAccessor, хотя все, что мне было нужно, уже было в TryHandleAsync из IExceptionHandler.
Вот мой код, который работает: В program.cs:
builder.Services.AddExceptionHandler<ErrorHandler>(); //custom error log, still needs UseExceptionHandler
var app = builder.Build();
app.UseExceptionHandler("/Home/Error");
И в моем общем классе обработки исключений ErrorHandler.cs:
public class ErrorHandler : IExceptionHandler
{
public ErrorHandler()
{
}
public ValueTask<bool> TryHandleAsync(
HttpContext httpContext,
Exception exception,
CancellationToken cancellationToken)
{
string errorReport = GetErrorReport(exception, httpContext);
SaveError(errorReport);
return ValueTask.FromResult(false);
}
private string GetErrorReport(Exception e, HttpContext httpContext)
{
StringBuilder sbErrorReport = new StringBuilder(1024);
try
{
string errorMessage;
string CurUrl = "";
string CurReferrer = "";
string reqBodyRaw = "";
string clientIP = "";
string extra = "";
var exceptionHandlerFeatures = httpContext.Features.Get<IExceptionHandlerFeature>();
//error message:
errorMessage = e.Message; //also check e.InnerException
//client IP
if (httpContext.Connection.RemoteIpAddress != null)
{
clientIP = httpContext.Connection.RemoteIpAddress.ToString();
}
//Referer / headers:
CurReferrer = httpContext.Request.Headers["Referer"].ToString();
//request posted body
using (var reader = new StreamReader(httpContext.Request.BodyReader.AsStream(false), Encoding.UTF8))
{
reqBodyRaw = reader.ReadToEnd();
}
//get the requested URL
if (exceptionHandlerFeatures != null)
{
//CurUrl = httpContext.Request.Path; //this does not work, gives the error page path.
CurUrl = exceptionHandlerFeatures.Path; //this does work.
}
// write details to sbErrorReport....
}
catch (Exception ex)
{
//nothing?
}
return sbErrorReport.ToString();
}
}
Получение IP-адреса клиента и Referer работает. Однако получение данных формы не работает. Мне нужны необработанные данные, отправленные POST на веб-страницу, для последующего анализа ошибок. Я попробовал и req.Form, и req.Body. req.Form не работает и выдает предупреждение: вызов этого свойства может привести к исчерпанию потока, поскольку оно является оберткой асинхронной реализации.