[英]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
}
}
問題是內容在ActionExecuting
或ActionExecuted
始終為空。
我認為這是因為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.