【问题标题】:View POST request body in Application Insights在 Application Insights 中查看 POST 请求正文
【发布时间】:2017-03-09 03:54:02
【问题描述】:

是否可以在 Application Insights 中查看 POST 请求正文?

我可以看到请求详细信息,但看不到应用程序洞察中发布的有效负载。我必须通过一些编码来跟踪它吗?

我正在构建一个 MVC 核心 1.1 Web Api。

【问题讨论】:

  • 请记住。请求中的数据可能包含私人和敏感信息,这些信息不供您阅读,也不存储在任何日志中。仅将其用于开发和测试目的。

标签: c# asp.net-core-mvc azure-application-insights


【解决方案1】:

您可以简单地实现自己的Telemetry Initializer

例如,在提取负载并将其添加为请求遥测的自定义维度的实现下面:

public class RequestBodyInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var requestTelemetry = telemetry as RequestTelemetry;
        if (requestTelemetry != null && (requestTelemetry.HttpMethod == HttpMethod.Post.ToString() || requestTelemetry.HttpMethod == HttpMethod.Put.ToString()))
        {
            using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
            {
                string requestBody = reader.ReadToEnd();
                requestTelemetry.Properties.Add("body", requestBody);
            }
        }
    }
}

然后通过configuration file 或通过代码将其添加到配置中:

TelemetryConfiguration.Active.TelemetryInitializers.Add(new RequestBodyInitializer());

然后在 Analytics 中查询:

requests | limit 1 | project customDimensions.body

【讨论】:

  • 看来 requestTelemetry.HttpMethod 已被弃用。我不确定去哪里查找 HttpMethod。
  • @ShyamalParikh 你可以使用 HttpContext.Current.Request.HttpMethod 而不是 requestTelemetry.HttpMethod
  • 请记住,此代码会执行两次:一次在请求之前,一次在请求之后。请求正文第二次为空,因此您可能需要在添加(或覆盖)属性之前检查输入流的长度!
  • 我必须重置请求 inputStream 的位置才能读取正文。 HttpContext.Current.Request.InputStream.Position = 0;
  • 我也可以写“响应正文”吗?如果可以,有人可以分享一些指南或代码 sn-p。
【解决方案2】:

@yonisha 提供的解决方案在我看来是最干净的。但是,您仍然需要在其中获取您的HttpContext,为此您需要更多代码。我还插入了一些基于或取自上述代码示例的 cmets。重置请求的位置很重要,否则您将丢失其数据。

这是我测试过的解决方案,并给了我 jsonbody:

public class RequestBodyInitializer : ITelemetryInitializer
{
    readonly IHttpContextAccessor httpContextAccessor;

    public RequestBodyInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        if (telemetry is RequestTelemetry requestTelemetry)
        {
            if ((httpContextAccessor.HttpContext.Request.Method == HttpMethods.Post ||
                 httpContextAccessor.HttpContext.Request.Method == HttpMethods.Put) &&
                httpContextAccessor.HttpContext.Request.Body.CanRead)
            {
                const string jsonBody = "JsonBody";

                if (requestTelemetry.Properties.ContainsKey(jsonBody))
                {
                    return;
                }

                //Allows re-usage of the stream
                httpContextAccessor.HttpContext.Request.EnableRewind();

                var stream = new StreamReader(httpContextAccessor.HttpContext.Request.Body);
                var body = stream.ReadToEnd();

                //Reset the stream so data is not lost
                httpContextAccessor.HttpContext.Request.Body.Position = 0;
                requestTelemetry.Properties.Add(jsonBody, body);
            }
        }
    }

然后一定要把它添加到你的 Startup -> ConfigureServices

services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();

编辑:

如果您还想获取响应正文,我发现创建一个 middleware(.NET Core,不确定框架)很有用。起初我采用了上述方法,您记录响应和请求,但大多数时候您希望将它们放在一起:

    public async Task Invoke(HttpContext context)
    {
        var reqBody = await this.GetRequestBodyForTelemetry(context.Request);

        var respBody = await this.GetResponseBodyForTelemetry(context);
        this.SendDataToTelemetryLog(reqBody, respBody, context);
    }

这等待请求和响应。 GetRequestBodyForTelemetry 与遥测初始化程序中的代码几乎相同,除了使用 Task。对于我使用下面代码的响应正文,我还排除了 204,因为这会导致 nullref:

public async Task<string> GetResponseBodyForTelemetry(HttpContext context)
{
    var originalBody = context.Response.Body;

    try
    {
        using (var memStream = new MemoryStream())
        {
            context.Response.Body = memStream;

            //await the responsebody
            await next(context);
            if (context.Response.StatusCode == 204)
            {
                return null;
            }

            memStream.Position = 0;
            var responseBody = new StreamReader(memStream).ReadToEnd();

            //make sure to reset the position so the actual body is still available for the client
            memStream.Position = 0;
            await memStream.CopyToAsync(originalBody);

            return responseBody;
        }
    }
    finally
    {
        context.Response.Body = originalBody;
    }
}

【讨论】:

  • 谢谢,您的代码有效。我也可以写“响应正文”吗?如果可以,请您分享一些指南或代码 sn-p(如果可能)。
  • 是的,这就是问题所在:)。但是,我确实修改了我的代码以仅记录一次项目。 @joerivrij
  • 仅供参考,自 .NET Core 3.0 起,EnableRewind() 已移至 httpContextAccessor.HttpContext.Request.EnableBuffering()
  • 我刚刚在一个 ASP .NET 5 项目中使用了这段代码,我有两个问题: 1) ReadToAll() 不允许,需要异步。 2)我总是得到错误:ObjectDisposedException:无法访问已处置的对象。对象名称:'HttpRequestStream'。
  • @JoanComasFdz:初始化程序不起作用,因为当它运行时,主体流已经被释放,导致请求已经被填充。您必须编写一个中间件。在中间件中获取遥测对象的神奇方法是httpContext.Features.Get&lt;RequestTelemetry&gt;()。有了这些知识和给定的答案,您应该可以让它发挥作用。
【解决方案3】:

几天前,我收到了类似的要求,即通过从有效负载中过滤掉敏感的输入用户数据,将请求正文记录到应用程序洞察中。所以分享我的解决方案。以下解决方案是为 ASP.NET Core 2.0 Web API 开发的。

ActionFilterAttribute

我使用了来自(Microsoft.AspNetCore.Mvc.Filters 命名空间)的ActionFilterAttribute,它通过ActionArgument 提供模型,以便通过反射提取那些标记为敏感的属性。

public class LogActionFilterAttribute : ActionFilterAttribute
{
    private readonly IHttpContextAccessor httpContextAccessor;

    public LogActionFilterAttribute(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (context.HttpContext.Request.Method == HttpMethods.Post || context.HttpContext.Request.Method == HttpMethods.Put)
        {
            // Check parameter those are marked for not to log.
            var methodInfo = ((Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor)context.ActionDescriptor).MethodInfo;
            var noLogParameters = methodInfo.GetParameters().Where(p => p.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute))).Select(p => p.Name);

            StringBuilder logBuilder = new StringBuilder();

            foreach (var argument in context.ActionArguments.Where(a => !noLogParameters.Contains(a.Key)))
            {
                var serializedModel = JsonConvert.SerializeObject(argument.Value, new JsonSerializerSettings() { ContractResolver = new NoPIILogContractResolver() });
                logBuilder.AppendLine($"key: {argument.Key}; value : {serializedModel}");
            }

            var telemetry = this.httpContextAccessor.HttpContext.Items["Telemetry"] as Microsoft.ApplicationInsights.DataContracts.RequestTelemetry;
            if (telemetry != null)
            {
                telemetry.Context.GlobalProperties.Add("jsonBody", logBuilder.ToString());
            }

        }

        await next();
    }
}

“LogActionFilterAttribute”作为过滤器注入到 MVC 管道中。

 services.AddMvc(options =>
 {
       options.Filters.Add<LogActionFilterAttribute>();
 });

NoLogAttribute

在上面的代码中,NoLogAttribute 属性被使用,应该应用于模型/模型的属性或方法参数以指示不应该记录值。

public class NoLogAttribute : Attribute
{
}

NoPIILogContractResolver

另外,NoPIILogContractResolver 在序列化过程中用于JsonSerializerSettings

internal class NoPIILogContractResolver : DefaultContractResolver
{
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization)
    {
        var properties = new List<JsonProperty>();

        if (!type.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute)))
        {
            IList<JsonProperty> retval = base.CreateProperties(type, memberSerialization);
            var excludedProperties = type.GetProperties().Where(p => p.GetCustomAttributes(true).Any(t => t.GetType() == typeof(NoLogAttribute))).Select(s => s.Name);
            foreach (var property in retval)
            {
                if (excludedProperties.Contains(property.PropertyName))
                {
                    property.PropertyType = typeof(string);
                    property.ValueProvider = new PIIValueProvider("PII Data");
                }

                properties.Add(property);
            }
        }

        return properties;
    }
}

internal class PIIValueProvider : IValueProvider
{
    private object defaultValue;

    public PIIValueProvider(string defaultValue)
    {
        this.defaultValue = defaultValue;
    }

    public object GetValue(object target)
    {
        return this.defaultValue;
    }

    public void SetValue(object target, object value)
    {

    }
}

PIITelemetryInitializer

要注入RequestTelemetry 对象,我必须使用ITelemetryInitializer 以便可以在LogActionFilterAttribute 类中检索RequestTelemetry

public class PIITelemetryInitializer : ITelemetryInitializer
{
    IHttpContextAccessor httpContextAccessor;

    public PIITelemetryInitializer(IHttpContextAccessor httpContextAccessor)
    {
        this.httpContextAccessor = httpContextAccessor;
    }

    public void Initialize(ITelemetry telemetry)
    {
        if (this.httpContextAccessor.HttpContext != null)
        {
            if (telemetry is Microsoft.ApplicationInsights.DataContracts.RequestTelemetry)
            {
                this.httpContextAccessor.HttpContext.Items.TryAdd("Telemetry", telemetry);
            }
        }
    }
}

PIITelemetryInitializer 注册为

services.AddSingleton<ITelemetryInitializer, PIITelemetryInitializer>();

测试功能

下面的代码演示了上面代码的用法

创建了一个控制器

[Route("api/[controller]")]
public class ValuesController : Controller
{
    private readonly ILogger _logger;

    public ValuesController(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<ValuesController>();
    }

    // POST api/values
    [HttpPost]
    public void Post([FromBody, NoLog]string value)
    {

    }

    [HttpPost]
    [Route("user")]
    public void AddUser(string id, [FromBody]User user)
    {

    }
}

其中User模型定义为

public class User
{
    [NoLog]
    public string Id { get; set; }

    public string Name { get; set; }

    public DateTime AnneviseryDate { get; set; }

    [NoLog]
    public int LinkId { get; set; }

    public List<Address> Addresses { get; set; }
}

public class Address
{
    public string AddressLine { get; set; }

    [NoLog]
    public string City { get; set; }

    [NoLog]
    public string Country { get; set; }
}

所以当 API 被 Swagger 工具调用时

jsonBody 是在 Request 中记录的,没有敏感数据。所有敏感数据都替换为“PII 数据”字符串文字。

【讨论】:

  • 其实你也可以使用context.HttpContext.Features.Get&lt;RequestTelemetry&gt;()来获取ActionFilterAttribute中的Telemetry Instance
  • @sk2andy 关于为什么 context.HttpContext.Features.Get&lt;RequestTelemetry&gt;() 在 prod 中为 null 的任何想法,密钥是正确的并且可以在 Dev 和 QA 环境中工作,只是 prod 不确定,但它的代码相同。
  • 在 .NET 5 中工作得很好,但您不需要提供 PIITelemetryInitializer ,因为它已经通过在 httpContext.Features.Get&lt;RequestTelemetry&gt;() 中执行 services.AddApplicationInsightsTelemetry() 提供
  • @Seabizkit 你在你的 Startup 中添加了services.AddApplicationInsights() 吗?
【解决方案4】:

更新:我已将以下逻辑放入现成的 NuGet 包中。您可以找到更多关于包here 和主题本身的信息here


我选择了自定义中间件路径,因为 HttpContext 已经存在,它使事情变得更容易。

public class RequestBodyLoggingMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var method = context.Request.Method;

        // Ensure the request body can be read multiple times
        context.Request.EnableBuffering();

        // Only if we are dealing with POST or PUT, GET and others shouldn't have a body
        if (context.Request.Body.CanRead && (method == HttpMethods.Post || method == HttpMethods.Put))
        {
            // Leave stream open so next middleware can read it
            using var reader = new StreamReader(
                context.Request.Body,
                Encoding.UTF8,
                detectEncodingFromByteOrderMarks: false,
                bufferSize: 512, leaveOpen: true);

            var requestBody = await reader.ReadToEndAsync();

            // Reset stream position, so next middleware can read it
            context.Request.Body.Position = 0;

            // Write request body to App Insights
            var requestTelemetry = context.Features.Get<RequestTelemetry>();                              
            requestTelemetry?.Properties.Add("RequestBody", requestBody);
        }

        // Call next middleware in the pipeline
        await next(context);
    }
}

这就是我记录响应正文的方式

public class ResponseBodyLoggingMiddleware : IMiddleware
{
    public async Task InvokeAsync(HttpContext context, RequestDelegate next)
    {
        var originalBodyStream = context.Response.Body;

        try
        {
            // Swap out stream with one that is buffered and suports seeking
            using var memoryStream = new MemoryStream();
            context.Response.Body = memoryStream;

            // hand over to the next middleware and wait for the call to return
            await next(context);

            // Read response body from memory stream
            memoryStream.Position = 0;
            var reader = new StreamReader(memoryStream);
            var responseBody = await reader.ReadToEndAsync();

            // Copy body back to so its available to the user agent
            memoryStream.Position = 0;
            await memoryStream.CopyToAsync(originalBodyStream);

            // Write response body to App Insights
            var requestTelemetry = context.Features.Get<RequestTelemetry>();
            requestTelemetry?.Properties.Add("ResponseBody", responseBody);
        }
        finally
        {
            context.Response.Body = originalBodyStream;
        }
    }
}

比添加扩展方法...

public static class ApplicationInsightExtensions
{
    public static IApplicationBuilder UseRequestBodyLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<RequestBodyLoggingMiddleware>();
    }

    public static IApplicationBuilder UseResponseBodyLogging(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<ResponseBodyLoggingMiddleware>();
    }            
}

...允许在 Startup.cs 内部进行干净的集成

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
        
        // Enable our custom middleware
        app.UseRequestBodyLogging();
        app.UseResponseBodyLogging();
    }
    
    // ...
}

不要忘记在ConfigureServices()中注册自定义中间件组件

public void ConfigureServices(IServiceCollection services)
{
    // ...
    services.AddApplicationInsightsTelemetry(Configuration["APPINSIGHTS_CONNECTIONSTRING"]);
            
    services.AddTransient<RequestBodyLoggingMiddleware>();
    services.AddTransient<ResponseBodyLoggingMiddleware>();
}

【讨论】:

  • 这仅适用于 .NET 5。非常感谢!
  • 很棒的文章。感谢分享您的工作!
  • 在 .NET 5 上为我工作,谢谢!
【解决方案5】:

在 Asp.Net 核心中,看起来我们不必使用 ITelemetryInitializer。我们可以使用中间件将请求记录到应用程序洞察中。感谢@IanKemp https://github.com/microsoft/ApplicationInsights-aspnetcore/issues/686

 public async Task Invoke(HttpContext httpContext)
    {
        var requestTelemetry = httpContext.Features.Get<RequestTelemetry>();

        //Handle Request 
        var request = httpContext.Request;
        if (request?.Body?.CanRead == true)
        {
            request.EnableBuffering();

            var bodySize = (int)(request.ContentLength ?? request.Body.Length);
            if (bodySize > 0)
            {
                request.Body.Position = 0;

                byte[] body;

                using (var ms = new MemoryStream(bodySize))
                {
                    await request.Body.CopyToAsync(ms);

                    body = ms.ToArray();
                }

                request.Body.Position = 0;

                if (requestTelemetry != null)
                {
                    var requestBodyString = Encoding.UTF8.GetString(body);

                    requestTelemetry.Properties.Add("RequestBody", requestBodyString);
                }
            }
        }

        await _next(httpContext); // calling next middleware
    }

【讨论】:

  • 你在哪里使用它?以及如何将这个方法添加到中间件中?
  • 这个:httpContext.Features.Get&lt;RequestTelemetry&gt;()给我返回null,是否需要额外配置?
  • @Ramūnas 你知道我们的 dev 和 qa 都很好,但 prod 是空的
  • @Seabizkit 我使用不同的方法提供here:
【解决方案6】:

我为此实现了一个中间件,

调用方法可以,

 if (context.Request.Method == "POST" || context.Request.Method == "PUT")
        {
            var bodyStr = GetRequestBody(context);
            var telemetryClient = new TelemetryClient();
            var traceTelemetry = new TraceTelemetry
            {
                Message = bodyStr,
                SeverityLevel = SeverityLevel.Verbose
            };
            //Send a trace message for display in Diagnostic Search. 
            telemetryClient.TrackTrace(traceTelemetry);
        }

在哪里,GetRequestBody 是这样的,

private static string GetRequestBody(HttpContext context)
    {
        var bodyStr = "";
        var req = context.Request;

        //Allows using several time the stream in ASP.Net Core.
        req.EnableRewind();

        //Important: keep stream opened to read when handling the request.
        using (var reader = new StreamReader(req.Body, Encoding.UTF8, true, 1024, true))
        {
            bodyStr = reader.ReadToEnd();
        }

        // Rewind, so the core is not lost when it looks the body for the request.
        req.Body.Position = 0;
        return bodyStr;
    }

【讨论】:

  • req.EnableRewind()方法从何而来?
  • 在 Asp.Net 核心“Microsoft.AspNetCore.Http.Internal.BufferingHelper”中有这个 HttpRequest 的扩展方法。参考-docs.microsoft.com/en-us/aspnet/core/api/…
  • 考虑到您可以将 SDK 功能用于此类情况,这是一个复杂的解决方案。此外,您需要将跟踪遥测与相关的请求遥测相关联
  • @yonisha:“SDK 功能”是什么意思?
  • @Dhanuka777,请参阅上面的答案。 SDK 为此类情况提供了可扩展性选项,例如遥测初始化程序。请参阅我的答案中的示例
【解决方案7】:

我从来没有得到@yonisha 的答案,所以我改用DelegatingHandler

public class MessageTracingHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Trace the request
        await TraceRequest(request);

        // Execute the request
        var response = await base.SendAsync(request, cancellationToken);

        // Trace the response
        await TraceResponse(response);

        return response;
    }

    private async Task TraceRequest(HttpRequestMessage request)
    {
        try
        {
            var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();

            var requestTraceInfo = request.Content != null ? await request.Content.ReadAsByteArrayAsync() : null;

            var body = requestTraceInfo.ToString();

            if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
            {
                requestTelemetry.Properties.Add("Request Body", body);
            }
        }
        catch (Exception exception)
        {
            // Log exception
        }
    }

    private async Task TraceResponse(HttpResponseMessage response)
    {
        try
        {
            var requestTelemetry = HttpContext.Current?.GetRequestTelemetry();

            var responseTraceInfo = response.Content != null ? await response.Content.ReadAsByteArrayAsync() : null;

            var body = responseTraceInfo.ToString();

            if (!string.IsNullOrWhiteSpace(body) && requestTelemetry != null)
            {
                requestTelemetry.Properties.Add("Response Body", body); 
            }
        }
        catch (Exception exception)
        {
            // Log exception
        }
    }
}

.GetRequestTelemetry()Microsoft.ApplicationInsights.Web 的扩展方法。

【讨论】:

  • 非常感谢您添加此解决方案,我有一个在 .net 框架中制作的 Web API 项目,我正在谷歌上搜索以找到解决方案,我在这里找到了您的帖子,我添加了相同的代码,我可以记录 Web API 请求的响应,如果此代码有任何更新或改进,请告诉我?
【解决方案8】:

我可以使用 @yonisha 方法在 Application Insights 中记录请求消息正文,但我无法记录响应消息正文。我有兴趣记录响应消息正文。我已经使用 @yonisha 方法记录了 Post、Put、Delete 请求消息正文。

当我尝试访问 TelemetryInitializer 中的响应正文时,我不断收到异常消息,提示“流不可读。当我研究更多时,我发现 AzureInitializer 作为 HttpModule(ApplicationInsightsWebTracking) 的一部分运行,所以通过它获取控制响应对象的时间被释放。

我从@Oskar 的回答中得到了一个想法。为什么没有委托处理程序并记录响应,因为响应对象没有在消息处理程序阶段进行处理。消息处理程序是 Web API 生命周期的一部分,即类似于 HTTP 模块,但仅限于 Web API。当我开发和测试这个想法时,幸运的是,它奏效了,我使用消息处理程序将响应记录在请求消息中,并在 AzureInitializer(HTTP 模块,其执行发生在消息处理程序之后)中检索它。这是示例代码。

public class AzureRequestResponseInitializer : ITelemetryInitializer
{
    public void Initialize(ITelemetry telemetry)
    {
        var requestTelemetry = telemetry as RequestTelemetry;
        if (requestTelemetry != null && HttpContext.Current != null && HttpContext.Current.Request != null)
        {
            if ((HttpContext.Current.Request.HttpMethod == HttpMethod.Post.ToString() 
                 || HttpContext.Current.Request.HttpMethod == HttpMethod.Put.ToString()) &&
                HttpContext.Current.Request.Url.AbsoluteUri.Contains("api"))
                using (var reader = new StreamReader(HttpContext.Current.Request.InputStream))
                {
                    HttpContext.Current.Request.InputStream.Position = 0;
                    string requestBody = reader.ReadToEnd();
                    if (requestTelemetry.Properties.Keys.Contains("requestbody"))
                    {
                        requestTelemetry.Properties["requestbody"] = requestBody;
                    }
                    else
                    {
                        requestTelemetry.Properties.Add("requestbody", requestBody);
                    }
                }
            else if (HttpContext.Current.Request.HttpMethod == HttpMethod.Get.ToString() 
                     && HttpContext.Current.Response.ContentType.Contains("application/json"))
            {
                var netHttpRequestMessage = HttpContext.Current.Items["MS_HttpRequestMessage"] as HttpRequestMessage;
                if (netHttpRequestMessage.Properties.Keys.Contains("responsejson"))
                {
                    var responseJson = netHttpRequestMessage.Properties["responsejson"].ToString();
                    if (requestTelemetry.Properties.Keys.Contains("responsebody"))
                    {
                        requestTelemetry.Properties["responsebody"] = responseJson;
                    }
                    else
                    {
                        requestTelemetry.Properties.Add("responsebody", responseJson);
                    }
                }
            }
        }

    }
}

config.MessageHandlers.Add(new LoggingHandler());

public class LoggingHandler : DelegatingHandler
{
    protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        return base.SendAsync(request, cancellationToken).ContinueWith(task =>
        {
            var response = task.Result;
            StoreResponse(response);
            return response;
        });
    }


    private void StoreResponse(HttpResponseMessage response)
    {
        var request = response.RequestMessage;

        (response.Content ?? new StringContent("")).ReadAsStringAsync().ContinueWith(x =>
        {
            var ctx = request.Properties["MS_HttpContext"] as HttpContextWrapper;

            if (request.Properties.ContainsKey("responseJson"))
            {
                request.Properties["responsejson"] = x.Result;
            }
            else
            {
                request.Properties.Add("responsejson", x.Result);
            }
        });
    }
}

【讨论】:

    【解决方案9】:

    yonisha 提供的解决方案很干净,但在 .Net Core 2.0 中对我不起作用。如果您有 JSON 正文,则此方法有效:

    public IActionResult MyAction ([FromBody] PayloadObject payloadObject)
    {
        //create a dictionary to store the json string
        var customDataDict = new Dictionary<string, string>();
    
        //convert the object to a json string
        string activationRequestJson = JsonConvert.SerializeObject(
        new
        {
            payloadObject = payloadObject
        });
    
        customDataDict.Add("body", activationRequestJson);
    
        //Track this event, with the json string, in Application Insights
        telemetryClient.TrackEvent("MyAction", customDataDict);
    
        return Ok();
    }
    

    【讨论】:

      【解决方案10】:

      很抱歉,@yonisha 的解决方案似乎不适用于 .NET 4.7。 Application Insights 部分工作正常,但实际上没有简单的方法可以在 .NET 4.7 的遥测初始化程序中获取请求正文。 .NET 4.7 使用 GetBufferlessInputStream() 来获取流,并且该流是“读取一次”。一个潜在的代码是这样的:

      private static void LogRequestBody(ISupportProperties requestTelemetry)
      {
          var requestStream = HttpContext.Current?.Request?.GetBufferlessInputStream();
      
          if (requestStream?.Length > 0)
              using (var reader = new StreamReader(requestStream))
              {
                  string body = reader.ReadToEnd();
                  requestTelemetry.Properties["body"] = body.Substring(0, Math.Min(body.Length, 8192));
              }
      }
      

      但是GetBufferlessInputStream()的返回已经被消耗,不支持查找。因此,正文将始终为空字符串。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-01-11
        • 1970-01-01
        • 2022-01-12
        • 1970-01-01
        • 2016-10-09
        • 2017-10-01
        • 2021-04-05
        相关资源
        最近更新 更多