c# - tutorial - Ver cuerpo de solicitud POST en Application Insights
Implementé un middleware para esto,
El método de invocación hace,
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);
}
Donde, GetRequestBody es como,
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;
}
La solución provista por @yonisha es, en mi opinión, la más limpia disponible. Sin embargo, aún necesitas ingresar tu httpcontext allí y para eso necesitas más código. También he insertado algunos comentarios que están basados o tomados de los ejemplos de código anteriores. Es importante restablecer la posición de su solicitud, de lo contrario perderá sus datos.
Esta es mi solución que he probado y me da el 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);
}
}
}
Entonces, también asegúrese de agregar esto a su Inicio -> Configurar Servicios
services.AddSingleton<ITelemetryInitializer, RequestBodyInitializer>();
EDITAR:
Si también desea obtener el cuerpo de respuesta, me ha resultado útil crear una pieza de middleware (dotnet core no está seguro acerca del marco). Al principio adopté el enfoque anterior en el que registra una respuesta y una solicitud, pero la mayoría de las veces desea que estén juntos.
public async Task Invoke(HttpContext context)
{
var reqBody = await this.GetRequestBodyForTelemetry(context.Request);
var respBody = await this.GetResponseBodyForTelemetry(context);
this.SendDataToTelemetryLog(reqBody, respBody, context);
}
Esto espera una Solicitud y una Respuesta donde la solicitud es más o menos la misma que la anterior, en lugar de ser una tarea.
Para el cuerpo de respuesta, he usado el código de abajo, también excluí un 204 ya que eso lleva a un 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;
}
}
La solución provista por yonisha es limpia, pero no funciona para mí en .Net Core 2.0. Esto funciona si tienes un cuerpo 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();
}
Lo siento, la solución de @ yonisha no parece funcionar en .NET 4.7. La parte de Application Insights funciona bien, pero en realidad no hay una forma sencilla de obtener el cuerpo de la solicitud dentro del inicializador de telemetría en .NET 4.7. .NET 4.7 usa GetBufferlessInputStream () para obtener el flujo, y este flujo es "leído una vez". Un código potencial es así:
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));
}
}
Pero el retorno de GetBufferlessInputStream () ya se ha calculado y no admite la búsqueda. Por lo tanto, el cuerpo siempre será una cadena vacía.
Nunca conseguí que la respuesta de @ yonisha funcionara, así que utilicé un 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()
es un método de extensión de Microsoft.ApplicationInsights.Web .
Simplemente puede implementar su propio Inicializador de telemetría :
Por ejemplo, debajo de una implementación que extrae la carga útil y la agrega como una dimensión personalizada de la telemetría de solicitud:
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);
}
}
}
}
Luego agréguelo a la configuración ya sea por el archivo de configuración o por medio de un código:
TelemetryConfiguration.Active.TelemetryInitializers.Add(new RequestBodyInitializer());
Luego consulta en Analytics:
requests | limit 1 | project customDimensions.body