簡體   English   中英

Web Api 請求內容在操作過濾器中為空

[英]Web Api Request Content is empty in action filter

我有一個名為Log的屬性,它嘗試將請求和響應的內容記錄到文本文件中。 我已經把它放在我的控制器上以覆蓋所有的動作。 LogAttribute我將內容作為字符串 ( ReadAsStringAsync ) 讀取,因此我不會丟失請求正文。

public class LogAttribute : ActionFilterAttribute
{
    // ..
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        // stuff goes here
        var content = actionContext.Request.Content.ReadAsStringAsync().Result; 
        // content is always empty because request body is cleared
    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        // other stuff goes here
        var content = actionContext.Request.Content.ReadAsStringAsync().Result;
        // content is always empty because request body is cleared
    }

    // ..
}

另一方面,我已將FromBody屬性放在我的操作參數類之前,以利用其優勢。

[Log]
public class SomethingController
{
    public HttpResponseMessage Foo([FromBody] myModel)
    {
        // something
    }
}

問題是內容在ActionExecutingActionExecuted始終為空。

我認為這是因為FromBody在我的Log屬性之前運行,這與它們在代碼中的順序不同。 我再次認為這是因為根據動作參數(路由處理)為請求找到最佳動作/控制器匹配。 之后我的請求正文被清除,因為請求正文在 WebApi 中沒有緩沖。

我想知道是否有任何方法可以更改FromBody屬性和我的Log屬性的運行時順序? 或其他解決問題的方法! 我應該提到我不想刪除FromBody並使用HttpRequestMessage而不是我的模型或類似的東西。

請求體是一個不可重繞的流; 它只能讀取一次。 格式化程序已經讀取了流並填充了模型。 我們無法在操作過濾器中再次讀取流。

你可以試試:

public class LogAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        var myModel = actionContext.ActionArguments["myModel"]; 

    }

    public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
    {
        var myModel = actionContext.ActionArguments["myModel"]; 
    }
}

實際上, ActionArguments只是一個字典,如果我們需要避免硬編碼參數名稱( "myModel" ),我們可以循環它。 當我們創建一個通用動作過濾器,需要針對某些特定要求處理一類相似對象時,我們可以讓我們的模型實現一個接口 => 知道哪個參數是我們需要處理的模型,但我們可以調用這些方法界面。

示例代碼:

public class LogAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(HttpActionContext actionContext)
        {
            foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable)))
            {
                 ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable
                 //do something with it. Maybe call model.log
            }
        }

        public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
        {
            foreach(var argument in actionContext.ActionArguments.Values.Where(v => v is ILogable)))
            {
                 ILogable model = argument as ILogable;//assume that only objects implementing this interface are logable
                 //do something with it. Maybe call model.log
            }
        }
    }

這種方法對我有用:

using (var stream = new MemoryStream())
{
    var context = (HttpContextBase)Request.Properties["MS_HttpContext"];
    context.Request.InputStream.Seek(0, SeekOrigin.Begin);
    context.Request.InputStream.CopyTo(stream);
    string requestBody = Encoding.UTF8.GetString(stream.ToArray());
}

為我返回觸發日志記錄或異常情況的操作參數對象的 json 表示。

在此處找到可接受的答案

public class ContentInterceptorHandler : DelegatingHandler
{
    protected override async Task<HttpResponseMessage> SendAsync(
        HttpRequestMessage request, CancellationToken cancellationToken)
    {
        if (request.Content != null)
        {
            var requestBody = await request.Content.ReadAsStringAsync();
            request.Properties["Content"] = requestBody;
            request.Content = new StringContent(requestBody, Encoding.UTF8, request.Content.Headers.ContentType.MediaType);
        }

        return await base.SendAsync(request, cancellationToken);
    }
}

public class LogRequestAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (!actionContext.Request.Properties.TryGetValue("Content", out var body))
            return;

        Console.WriteLine(body);
    }
}

並添加啟動

httpConfiguration.MessageHandlers.Add(new ContentInterceptorHandler());

這對我有用:

public override async Task OnActionExecutedAsync(HttpActionExecutedContext context, CancellationToken cancellationToken) {

                var requestLog = context.Request;
                if (requestLog != null)
                {
                    _logger.DebugFormat("Request: {0}", requestLog?.ToString());
                    var requestBody = context.ActionContext?.ActionArguments;
                    if (requestBody != null)
                    {
                        _logger.DebugFormat("Body: {0}", JsonConvert.SerializeObject(requestBody));
                    }
                }                   
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM