在Application Insights中查看POST请求正文

时间:2017-03-09 03:54:02

标签: c# azure-application-insights

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

我可以看到请求详细信息,但不能查看应用程序洞察中发布的有效负载。我是否需要通过编码来跟踪这个?

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

POST request

10 个答案:

答案 0 :(得分:26)

您可以简单地实现自己的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());

然后在Google Analytics中查询:

requests | limit 1 | project customDimensions.body

答案 1 :(得分:14)

@yonisha提供的解决方案在我看来是最干净的解决方案。但是你仍然需要在那里获得你的httpcontext,为此你需要更多的代码。我还插入了一些基于或取自上面的代码示例的注释。重置您的请求的位置非常重要,否则您将丢失其数据。

这是我测试过的解决方案,并给了我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);
            }
        }
    }

然后还要确保将此添加到您的启动 - > ConfigureServices

services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();

编辑:

如果您还想获得响应体,我发现创建一个中间件(dotnet核心不确定框架)是有用的。首先,我采用了上述方法来记录响应和请求,但大部分时间都需要这些方法。

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

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

这等待请求和响应,其中请求与上面的请求几乎相同,而不是它是一项任务。

对于我使用下面代码的响应主体,我也排除了204,因为它导致了nullref:

public async Task<string> GetResponseBodyForTelemetry(HttpContext context)
{
    Stream 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;
        }
    }

答案 2 :(得分:5)

我选择自定义中间件路径,因为 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>();
}

您可以阅读更多相关信息here

答案 3 :(得分:3)

在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
    }

答案 4 :(得分:2)

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

调用方法,

 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;
    }

答案 5 :(得分:1)

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();
}

答案 6 :(得分:0)

对不起,@ 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()的返回已被消耗,并且不支持搜索。因此,身体将永远是一个空字符串。

答案 7 :(得分:0)

我从来没有得到@ 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的扩展方法。

答案 8 :(得分:0)

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

ActionFilterAttribute

我使用了public class MyNewRequirement : AuthorizationHandler<MyRequirement>, IAuthorizationRequirement { protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MyRequirement requirement) { var mvcContext = context.Resource as AuthorizationFilterContext; //required service var _mvcOptions = mvcContext.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value; var parameterBinder = mvcContext.HttpContext.RequestServices.GetRequiredService<ParameterBinder>(); var _modelBinderFactory = mvcContext.HttpContext.RequestServices.GetRequiredService<IModelBinderFactory>(); var _modelMetadataProvider = mvcContext.HttpContext.RequestServices.GetRequiredService<IModelMetadataProvider>(); var controllerContext = new ControllerContext(mvcContext); controllerContext.ValueProviderFactories = new CopyOnWriteList<IValueProviderFactory>(_mvcOptions.ValueProviderFactories.ToArray()); var valueProvider = await CompositeValueProvider.CreateAsync(controllerContext); var parameters = controllerContext.ActionDescriptor.Parameters; var parameterBindingInfo = GetParameterBindingInfo( _modelBinderFactory, _modelMetadataProvider, controllerContext.ActionDescriptor, _mvcOptions); for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; var bindingInfo = parameterBindingInfo[i]; var modelMetadata = bindingInfo.ModelMetadata; if (!modelMetadata.IsBindingAllowed) { continue; } var model = await parameterBinder.BindModelAsync( controllerContext, bindingInfo.ModelBinder, valueProvider, parameter, modelMetadata, value: null); } } private static BinderItem[] GetParameterBindingInfo( IModelBinderFactory modelBinderFactory, IModelMetadataProvider modelMetadataProvider, ControllerActionDescriptor actionDescriptor, MvcOptions mvcOptions) { var parameters = actionDescriptor.Parameters; if (parameters.Count == 0) { return null; } var parameterBindingInfo = new BinderItem[parameters.Count]; for (var i = 0; i < parameters.Count; i++) { var parameter = parameters[i]; ModelMetadata metadata; if (mvcOptions.AllowValidatingTopLevelNodes && modelMetadataProvider is ModelMetadataProvider modelMetadataProviderBase && parameter is ControllerParameterDescriptor controllerParameterDescriptor) { // The default model metadata provider derives from ModelMetadataProvider // and can therefore supply information about attributes applied to parameters. metadata = modelMetadataProviderBase.GetMetadataForParameter(controllerParameterDescriptor.ParameterInfo); } else { // For backward compatibility, if there's a custom model metadata provider that // only implements the older IModelMetadataProvider interface, access the more // limited metadata information it supplies. In this scenario, validation attributes // are not supported on parameters. metadata = modelMetadataProvider.GetMetadataForType(parameter.ParameterType); } var binder = modelBinderFactory.CreateBinder(new ModelBinderFactoryContext { BindingInfo = parameter.BindingInfo, Metadata = metadata, CacheToken = parameter, }); parameterBindingInfo[i] = new BinderItem(binder, metadata); } return parameterBindingInfo; } private struct BinderItem { public BinderItem(IModelBinder modelBinder, ModelMetadata modelMetadata) { ModelBinder = modelBinder; ModelMetadata = modelMetadata; } public IModelBinder ModelBinder { get; } public ModelMetadata ModelMetadata { get; } } } 命名空间中的ActionFilterAttribute,它通过Microsoft.AspNetCore.Mvc.Filters提供了模型,以便通过反射可以提取那些标记为敏感的属性。

ActionArgument

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

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();
    }
}

NoLogAttribute

在以上代码中,使用 services.AddMvc(options => { options.Filters.Add<LogActionFilterAttribute>(); }); 属性,该属性应应用于模型/模型的属性或方法参数以指示不应记录该值。

NoLogAttribute

NoPIILogContractResolver

此外,public class NoLogAttribute : Attribute { } 在序列化过程中用于NoPIILogContractResolver

JsonSerializerSettings

PIITelemetryInitializer

要注入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) { } } 对象,我必须使用RequestTelemetry,以便可以在ITelemetryInitializer类中检索RequestTelemetry

LogActionFilterAttribute

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

因此,当Swagger工具调用API时

enter image description here

jsonBody已登录到请求中,没有敏感数据。所有敏感数据都将替换为“ PII Data”字符串文字。

enter image description here

答案 9 :(得分:0)

我可以使用@yonisha方法在Application Insights中记录请求消息正文,但不能记录响应消息正文。我对记录响应消息正文感兴趣。我已经使用@yonisha方法记录了发布,放置,删除请求消息的正文。

当我尝试访问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);
            }
        });
    }
}