简体   繁体   English

在ActionFilterAttribute中读取Asp.Net核心响应主体

[英]Read Asp.Net Core Response body in ActionFilterAttribute

I'm using Asp.Net Core as a Rest Api Service. 我使用Asp.Net Core作为Rest Api服务。 I need access to request and response in ActionFilter. 我需要在ActionFilter中访问请求和响应。 Actually, I found the request in OnActionExcecuted but I can't read the response result. 实际上,我在OnActionExcecuted中找到了请求,但我无法读取响应结果。

I'm trying to return value as follow: 我正在尝试返回如下值:

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

And in ActionFilter OnExcecuted method as follow: 并在ActionFilter OnExcecuted方法中如下:

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

I'm trying to get the response in ReadAsString as an Extension method as follow: 我正在尝试将ReadAsString中的响应作为Extension方法获取,如下所示:

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

But, there is no result! 但是,没有结果!

How I can get the response in OnActionExcecuted? 我如何在OnActionExcecuted中获得响应?

Thanks, everyone for taking the time to try and help explain 谢谢,每个人都花时间尝试帮助解释

If you're logging for json result/ view result , you don't need to read the whole response stream. 如果要记录json结果/视图结果,则无需读取整个响应流。 Simply serialize the context.Result : 只需序列化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("...");
        }
    }

I know there is already an answer but I want to also add that the problem is the MVC pipeline has not populated the Response.Body when running an ActionFilter so you cannot access it. 我知道已经有了答案,但我想补充一点,问题是MVC管道在运行ActionFilter时没有填充Response.Body ,因此您无法访问它。 The Response.Body is populated by the MVC middleware . Response.Body由MVC middleware填充。

If you want to read Response.Body then you need to create your own custom middleware to intercept the call when the Response object has been populated. 如果您想阅读Response.Body那么您需要创建自己的自定义中间件,以便在填充Response对象时拦截调用。 There are numerous websites that can show you how to do this. 有很多网站可以告诉你如何做到这一点。 One example is here . 这里有一个例子。

As discussed in the other answer, if you want to do it in an ActionFilter you can use the context.Result to access the information. 正如在另一个答案中所讨论的,如果您想在ActionFilter执行此操作,则可以使用context.Result来访问信息。

For logging whole request and response in the ASP.NET Core filter pipeline you can use Result filter attribute 要在ASP.NET Core过滤器管道中 记录整个请求和响应 ,可以使用Result过滤器属性

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

In Startup.cs add 在Startup.cs中添加

    app.UseMiddleware<ResponseRewindMiddleware>();

    services.AddScoped<LogRequestResponseAttribute>();

Somewhere add static class 在某处添加静态类

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

        } 

You can also do... 你也可以......

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

I used this in a .net core app. 我在.net核心应用程序中使用了它。

Hope it helps. 希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 在 ASP.NET 核心中重新读取请求正文 - Re-read a Request body in ASP.NET core Asp.Net MVC 4-ActionFilterAttribute用法 - Asp.Net MVC 4 - ActionFilterAttribute Usage ASP.NET MVC3 + ActionFilterAttribute +注入? - ASP.NET MVC3 + ActionFilterAttribute + Injection? 使用 ActionFilterAttribute 向 ASP.NET Core 中的自定义授权策略公开特定于操作的数据 - Exposing Action-Specific Data to Custom Authorization Policies in ASP.NET Core using ActionFilterAttribute ASP.NET MVC-在RegisterGlobalFilters()中使用ActionFilterAttribute时Response.Filter为null - ASP.NET MVC - Response.Filter is null when using ActionFilterAttribute in RegisterGlobalFilters() 在 Asp.Net 中有效地转发响应体 - Efficiently Forwarding a Response Body in Asp.Net 如何访问 ASP.NET 中的响应正文 - How to access response body in ASP.NET 如何配置ASP.NET Core来处理循环引用而不破坏正文的响应? - How to configure ASP.NET Core to handle circular references without breaking the body's response? 在ASP.NET中读取请求主体 - Read Request Body in ASP.NET 如何从 ASP.NET 核心中的 JSON 请求正文中读取多个参数? - How do I read multiple parameters from a JSON request body in ASP.NET Core?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM