web-dev-qa-db-fra.com

Lire le corps de réponse Asp.Net Core dans ActionFilterAttribute

J'utilise Asp.Net Core comme un service Rest Api. J'ai besoin d'accéder à la demande et à la réponse dans ActionFilter. En fait, j'ai trouvé la demande dans OnActionExcecuted mais je ne peux pas lire le résultat de la réponse.

J'essaie de retourner la valeur comme suit:

[HttpGet]
[ProducesResponseType(typeof(ResponseType), (int)HttpStatusCode.OK)]
[Route("[action]")]
public async Task<IActionResult> Get(CancellationToken cancellationToken)
{
    var model = await _responseServices.Get(cancellationToken);
    return Ok(model);
}

Et dans la méthode ActionFilter OnExcecuted comme suit:

_request = context.HttpContext.Request.ReadAsString().Result;
_response = context.HttpContext.Response.ReadAsString().Result; //?

J'essaie d'obtenir la réponse dans ReadAsString comme méthode d'extension comme suit:

public static async Task<string> ReadAsString(this HttpResponse response)
{
     var initialBody = response.Body;
     var buffer = new byte[Convert.ToInt32(response.ContentLength)];
     await response.Body.ReadAsync(buffer, 0, buffer.Length);
     var body = Encoding.UTF8.GetString(buffer);
     response.Body = initialBody;
     return body;
 }

Mais, il n'y a aucun résultat!

Comment puis-je obtenir la réponse dans OnActionExcecuted?

Merci à tous d'avoir pris le temps d'essayer d'aider à expliquer

7
Saeid Mirzaei

Si vous vous connectez pour le résultat json/afficher le résultat, vous n'avez pas besoin de lire l'intégralité du flux de réponse. Sérialisez simplement le context.Result:

public class MyFilterAttribute : ActionFilterAttribute
{
    private ILogger<MyFilterAttribute> logger;

    public MyFilterAttribute(ILogger<MyFilterAttribute> logger){
        this.logger = logger;
    }
    public override void OnActionExecuted(ActionExecutedContext context)
    {
        var result = context.Result;
        if (result is JsonResult json)
        {
            var x = json.Value;
            var status = json.StatusCode;
            this.logger.LogInformation(JsonConvert.SerializeObject(x));
        }
        if(result is ViewResult view){
            // I think it's better to log ViewData instead of the finally rendered template string
            var status = view.StatusCode;
            var x = view.ViewData;
            var name = view.ViewName;
            this.logger.LogInformation(JsonConvert.SerializeObject(x));
        }
        else{
            this.logger.LogInformation("...");
        }
    }
8
itminus

Je sais qu'il existe déjà une réponse, mais je veux également ajouter que le problème est que le pipeline MVC n'a pas rempli le Response.Body lors de l'exécution d'un ActionFilter afin que vous ne puissiez pas y accéder. Le Response.Body est rempli par le MVC middleware.

Si vous voulez lire Response.Body, vous devez ensuite créer votre propre middleware personnalisé pour intercepter l'appel lorsque l'objet Response a été rempli. Il existe de nombreux sites Web qui peuvent vous montrer comment procéder. Un exemple est ici .

Comme indiqué dans l'autre réponse, si vous voulez le faire dans un ActionFilter, vous pouvez utiliser le context.Result pour accéder aux informations.

3
Simply Ged

Pour journalisation de la demande et de la réponse entières dans le pipeline de filtre ASP.NET Core vous pouvez utiliser attribut de filtre de résultat

    public class LogRequestResponseAttribute : TypeFilterAttribute
    {
        public LogRequestResponseAttribute() : base(typeof(LogRequestResponseImplementation)) { }

        private class LogRequestResponseImplementation : IAsyncResultFilter
        {
            public async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
            {
                var requestHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Request.Headers);
                Log.Information("requestHeaders: " + requestHeadersText);

                var requestBodyText = await CommonLoggingTools.FormatRequestBody(context.HttpContext.Request);
                Log.Information("requestBody: " + requestBodyText);

                await next();

                var responseHeadersText = CommonLoggingTools.SerializeHeaders(context.HttpContext.Response.Headers);
                Log.Information("responseHeaders: " + responseHeadersText);

                var responseBodyText = await CommonLoggingTools.FormatResponseBody(context.HttpContext.Response);
                Log.Information("responseBody: " + responseBodyText);
            }
        }
    }

Dans Startup.cs, ajoutez

    app.UseMiddleware<ResponseRewindMiddleware>();

    services.AddScoped<LogRequestResponseAttribute>();

Ajoutez quelque part une classe statique

    public static class CommonLoggingTools
    {
        public static async Task<string> FormatRequestBody(HttpRequest request)
        {
            //This line allows us to set the reader for the request back at the beginning of its stream.
            request.EnableRewind();

            //We now need to read the request stream.  First, we create a new byte[] with the same length as the request stream...
            var buffer = new byte[Convert.ToInt32(request.ContentLength)];

            //...Then we copy the entire request stream into the new buffer.
            await request.Body.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false);

            //We convert the byte[] into a string using UTF8 encoding...
            var bodyAsText = Encoding.UTF8.GetString(buffer);

            //..and finally, assign the read body back to the request body, which is allowed because of EnableRewind()
            request.Body.Position = 0;

            return $"{request.Scheme} {request.Host}{request.Path} {request.QueryString} {bodyAsText}";
        }

        public static async Task<string> FormatResponseBody(HttpResponse response)
        {
            //We need to read the response stream from the beginning...
            response.Body.Seek(0, SeekOrigin.Begin);

            //...and copy it into a string
            string text = await new StreamReader(response.Body).ReadToEndAsync();

            //We need to reset the reader for the response so that the client can read it.
            response.Body.Seek(0, SeekOrigin.Begin);

            response.Body.Position = 0;

            //Return the string for the response, including the status code (e.g. 200, 404, 401, etc.)
            return $"{response.StatusCode}: {text}";
        }

        public static string SerializeHeaders(IHeaderDictionary headers)
        {
            var dict = new Dictionary<string, string>();

            foreach (var item in headers.ToList())
            {
                //if (item.Value != null)
                //{
                var header = string.Empty;
                foreach (var value in item.Value)
                {
                    header += value + " ";
                }

                // Trim the trailing space and add item to the dictionary
                header = header.TrimEnd(" ".ToCharArray());
                dict.Add(item.Key, header);
                //}
            }

            return JsonConvert.SerializeObject(dict, Formatting.Indented);
        }
    }

    public class ResponseRewindMiddleware {
        private readonly RequestDelegate next;

        public ResponseRewindMiddleware(RequestDelegate next) {
            this.next = next;
        }

        public async Task Invoke(HttpContext context) {

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

                    memStream.Position = 0;
                    await memStream.CopyToAsync(originalBody);
                }

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

        } 

1
Stefan Varga

Vous pouvez aussi faire ...

string response = "Hello";
if (result is ObjectResult objectResult)
        {
            var status = objectResult.StatusCode;
            var value = objectResult.Value;
            var stringResult = objectResult.ToString();
            responce = (JsonConvert.SerializeObject(value));
        }

Je l'ai utilisé dans une application de base .net.

J'espère que cela aide.

0
Bukunmi