Is there any way to get request body in .NET Core FilterAttribute?

后端 未结 4 1490
暗喜
暗喜 2021-02-08 23:26

Sample of my request

http://localhost:8065/api/note
POST
content-type:application/json
request body: { \"id\" : \"1234\", \"title\" : \"test\", \"status\" : \"dr         


        
相关标签:
4条回答
  • 2021-02-08 23:40

    If you're using IActionResult in your controllers and you want the .NET objects, you can write a filter like this:

    public class SampleFilter : IActionFilter
    {
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Result is ObjectResult)
            {
                var objResult = (ObjectResult)context.Result;
            }
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
    
        }
    }
    

    By the point it hits OnActionExecuted, the ObjectResult task has already completed, so you can just extract the value. You can also get the StatusCode with objResult.StatusCode.

    In the controller, return Ok(...) actually creates an OkObjectResult, etc.

    If you specifically want the serialzied result, then Set's answer is more valid.

    0 讨论(0)
  • 2021-02-08 23:51
        in .net core 3.1, I usually use this approach to handle this scenario
        
        1- Create Async Filter Attribute
        
        public class MyFilter : IAsyncAuthorizationFilter
            {
                private string _errorMessage = UserAccessErrorMessages.NO_ACCESS;
                public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
                {
                    string requestBody = await ReadBodyAsString(context.HttpContext.Request);
                    var reportFiltration = JsonConvert.DeserializeObject<YourModel>(requestBody);
                    var _myService = (IMyService)context.HttpContext.RequestServices.GetService(typeof(IMyService));
                        if (!hasAccess)
                        {
                            context.Result = new UnauthorizedObjectResult(_errorMessage);
                        }
                }
        
                private async Task<string> ReadBodyAsString(Microsoft.AspNetCore.Http.HttpRequest request)
                {
                    var initialBody = request.Body; // Workaround
        
                    try
                    {
                        //request.EnableRewind();
        
                        using (StreamReader reader = new StreamReader(request.Body))
                        {
                            string text = await reader.ReadToEndAsync();
                            return text;
                        }
                    }
                    finally
                    {
                        // Workaround so MVC action will be able to read body as well
                        request.Body = initialBody;
                    }
                }
            }
    2- Create Your Custom Attribute
    public class MyAttribute : TypeFilterAttribute, IAllowAnonymous
        {
            public MyAttribute () : base(typeof(MyFilter))
            {
    
            }
        }
    

    3- use your filter

        [HttpPost]
        [ActionName("MyAction")]
        [MyAttribute]
        public async Task<IActionResult> PostData([FromBody]MyModel model)
    
    0 讨论(0)
  • 2021-02-08 23:53

    Following snippet worked for me, to log request only if there is any exception.(.Net Core 3.1)

    {

    public class ExceptionFilter : IActionFilter
    {
        private ConcurrentDictionary<string, string> requests = new ConcurrentDictionary<string, string>();
    
        public void OnActionExecuted(ActionExecutedContext context)
        {
            if (context.Exception != null)
    
            {
                StringBuilder parameters = new StringBuilder();
    
                _logger.LogError("Error while executing action:" + context.ActionDescriptor.DisplayName);
    
                string errRequest;
                if(requests.TryGetValue(context.HttpContext.TraceIdentifier,out errRequest))
                {
                    _logger.LogError(errRequest);
                }
    
                _logger.LogError(context.Exception);
    
                context.Result = new ObjectResult("Error!!!")
                {
                    StatusCode = 500,
                };
                context.ExceptionHandled = true;
            }
    
            string req;
                requests.Remove(context.HttpContext.TraceIdentifier, out req);
    
    
        }
    
        public void OnActionExecuting(ActionExecutingContext context)
        {
            StringBuilder sb = new StringBuilder();
            foreach (var arg in context.ActionArguments)
            {
    
                sb.Append(arg.Key.ToString() + ":" + Newtonsoft.Json.JsonConvert.SerializeObject(arg.Value) + "\n");
    
            }
            requests.TryAdd(context.HttpContext.TraceIdentifier, sb.ToString());
        }
    
    
    }
    

    }

    0 讨论(0)
  • 2021-02-08 23:55

    Accordingly to this "Best way to log/read request body in a middleware" thread, the following should work:

    // using Microsoft.AspNetCore.Http.Internal;
    
    public class SampleActionFilterAttribute : TypeFilterAttribute
    {
        ... 
    
        public void OnActionExecuting(ActionExecutedContext context)
        {
            // read body before MVC action execution
            string bodyData = ReadBodyAsString(context.HttpContext.Request);
        }
    
        private string ReadBodyAsString(HttpRequest request)
        {
            var initialBody = request.Body; // Workaround
    
            try
            {
                request.EnableRewind();
    
                using (StreamReader reader = new StreamReader(request.Body))
                {
                    string text = reader.ReadToEnd();
                    return text;
                }
            }
            finally
            {
                // Workaround so MVC action will be able to read body as well
                request.Body = initialBody; 
            }
    
            return string.Empty;
        }
     }
    

    Also similar approach described in Read request body twice SO post


    Update: above approach in ReadBodyAsString with will work if used in middleware, not in action filter. The difference is that when action filter is calling (even for OnActionExecuting), the body stream already has been read and [FromBody] model has been populated.

    The good nesw is that so it is possible to get model directly in action filter by using context.ActionArguments["<model_name>"]. In your case:

    public void OnActionExecuted(ActionExecutedContext context)
    {
       var model = context.ActionArguments["model"] as NoteModel;
    }
    
    0 讨论(0)
提交回复
热议问题