导语
Azure Application Insights 是一个非常强大的 APM 工具,用于监视 Web 应用程序。但是,并非我们需要的所有功能都是开箱即用的。例如,为了记录请求和响应正文,我们必须写一个自定义 ASP.NET Core 中间件,就像这篇文章说的那样。
我现在有一个使用 JWT 身份验证的 ASP.NET Core Web API 应用程序。当请求失败时,我想记录用户的身份以便调试。让我们看看如何操作吧~
https://www.azureblue.io/how-to-log-http-request-body-with-asp-net-core-application-insights/
扩展原版 Middleware
我需要修改来自 https://www.azureblue.io/how-to-log-http-request-body-with-asp-net-core-application-insights 的 ASP.NET Core Middleware。
原始代码将请求正文作为自定义属性(Custom Property)记录到 Azure Application Insights。
public class RequestBodyLoggingMiddleware : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var method = context.Request.Method;
context.Request.EnableBuffering();
if (context.Request.Body.CanRead && (method == HttpMethods.Post || method == HttpMethods.Put))
{
using var reader = new StreamReader(
context.Request.Body,
Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 512, leaveOpen: true);
var requestBody = await reader.ReadToEndAsync();
context.Request.Body.Position = 0;
var requestTelemetry = context.Features.Get<RequestTelemetry>();
requestTelemetry?.Properties.Add("RequestBody", requestBody);
}
await next(context);
}
}
我的 JWT 身份验证将用户属性添加到Claim中。
var claims = new List<Claim>
{
new("UserId", user.Id.ToString()),
new(ClaimTypes.Name, user.DisplayName),
new(ClaimTypes.Email, request.Email),
new(ClaimTypes.AuthenticationMethod, "Password"),
new("LastLoginTimeUtc", user.LastLoginTimeUtc.ToString())
};
这些Claim 可以从 HttpContext.User 属性获取。
因此,将用户信息记录到 Application Insights 中非常简单,只需从 Claims 中获取值并将它们序列化为 JSON。
if (context.User.Identity is { IsAuthenticated: true })
{
var userId = Guid.Parse(context.User.FindFirst(p => p.Type == "UserId")?.Value ?? string.Empty);
var userName = context.User.Identity?.Name;
var email = context.User.FindFirst(p => p.Type == ClaimTypes.Email)?.Value;
requestTelemetry?.Properties.Add("MpsUser", JsonSerializer.Serialize(new
{
userId,
userName,
}, MpsJsonSerializerOptions.Default));
}
最终的代码看起来像这样
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
var method = context.Request.Method;
context.Request.EnableBuffering();
if (context.Request.Body.CanRead && (method == HttpMethods.Post || method == HttpMethods.Put))
{
using var reader = new StreamReader(
context.Request.Body,
Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 512, leaveOpen: true);
var requestBody = await reader.ReadToEndAsync();
context.Request.Body.Position = 0;
var requestTelemetry = context.Features.Get<RequestTelemetry>();
requestTelemetry?.Properties.Add("RequestBody", requestBody);
if (context.User.Identity is { IsAuthenticated: true })
{
var userId = Guid.Parse(context.User.FindFirst(p => p.Type == "UserId")?.Value ?? string.Empty);
var userName = context.User.Identity?.Name;
var email = context.User.FindFirst(p => p.Type == ClaimTypes.Email)?.Value;
requestTelemetry?.Properties.Add("MpsUser", JsonSerializer.Serialize(new
{
userId,
userName,
}, MpsJsonSerializerOptions.Default));
}
}
await next(context);
}
还有一个地方我们需要注意。因为中间件需要获取用户信息,所以必须放在认证授权中间件之后。
app.UseAuthentication();
app.UseAuthorization();
app.UseRequestBodyLogging();
app.UseResponseBodyLogging();
有了这些,我们现在可以看到用户身份被记录到 Azure Application Insights 上。