【发布时间】:2022-02-11 22:59:00
【问题描述】:
我编写了自定义中间件,用于记录对我们 API 的请求和响应。
我意识到请求和响应不匹配,它们似乎混淆了,这很奇怪。
这似乎是在很短的时间内发出大量请求时发生的。不知道如何,但似乎在调用LogResponse 时,它与LogRequest 不在同一范围内。
我已经实现了一个版本:https://stackoverflow.com/a/43404745/2286743
我错过了什么吗?
Trimmed down version of actual code
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
namespace Middleware
{
public class Web
{
private readonly RequestDelegate _next;
private Logging.AuditLog _auditLog;
public Web(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
try
{
EndpointMetadataCollection endpointMetaData = context.Features.Get<IEndpointFeature>()?.Endpoint.Metadata;
context.Request.EnableBuffering();
await LogRequest(context);
await LogResponse(context);
}
catch (UnauthorizedAccessException)
{
throw;
}
catch (Exception ex)
{
//Custom exception logging here
}
}
public async Task LogRequest(HttpContext context)
{
IHttpRequestFeature features = context.Features.Get<IHttpRequestFeature>();
string url = $"{features.Scheme}://{context.Request.Host.Value}{features.RawTarget}";
IFormCollection form = null;
string formString = string.Empty;
if (context.Request.HasFormContentType)
{
form = context.Request.Form;
}
else
{
formString = await new StreamReader(context.Request.Body).ReadToEndAsync();
var injectedRequestStream = new MemoryStream();
byte[] bytesToWrite = Encoding.UTF8.GetBytes(formString);
injectedRequestStream.Write(bytesToWrite, 0, bytesToWrite.Length);
injectedRequestStream.Seek(0, SeekOrigin.Begin);
context.Request.Body = injectedRequestStream;
}
_auditLog = new Logging.AuditLog()
{
RemoteHost = context.Connection.RemoteIpAddress.ToString(),
HttpURL = url,
LocalAddress = context.Connection.LocalIpAddress.ToString(),
Headers = Newtonsoft.Json.JsonConvert.SerializeObject(context.Request.Headers),
Form = form != null ? Newtonsoft.Json.JsonConvert.SerializeObject(form) : formString
};
}
public async Task LogResponse(HttpContext context)
{
if (_auditLog == null)
{
await _next(context);
return;
}
Stream originalBody = context.Response.Body;
try
{
using (var memStream = new MemoryStream())
{
context.Response.Body = memStream;
await _next(context);
memStream.Position = 0;
string responseBody = new StreamReader(memStream).ReadToEnd();
_auditLog.ResponseStatusCode = context.Response.StatusCode;
_auditLog.ResponseBody = responseBody;
_auditLog = _auditLog.Save();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
}
catch
{
_auditLog?.Save();
throw;
}
finally
{
context.Response.Body = originalBody;
}
}
}
}
【问题讨论】:
-
我在
Invoke方法中没有看到任何await next() -
啊,它在
LogResponse中。不是最明显的地方,但无论如何......但你要调用它两次。请将await next(context)移动到Invoke方法,在LogRequest和LogResponse之间,并且只调用一次!!! -
@Pieterjan,你搞错了。当
_auditLog对象为空时,调用第一个await _next(context);。第二个是当记录过程继续并且它需要记录响应时。它也没有错误。
标签: c# .net-6.0 asp.net-core-middleware