私のリクエストのサンプル
http://localhost:8065/api/note
POST
content-type:application/json
request body: { "id" : "1234", "title" : "test", "status" : "draft"}
そして応答は
{ "msg" : "ok", "code" : 1}
アクション
public async Task<IActionResult> Post([FromBody]NoteModel model)
すべてのリクエストを自動的にログに記録するために、このジョブを実行する属性を作成します。属性は次のようになります:(from Microsoft Docs )
public class SampleActionFilterAttribute : TypeFilterAttribute
{
public SampleActionFilterAttribute():base(typeof(SampleActionFilterImpl))
{
}
private class SampleActionFilterImpl : IActionFilter
{
private readonly ILogger _logger;
public SampleActionFilterImpl(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
}
public void OnActionExecuting(ActionExecutingContext context)
{
}
public void OnActionExecuted(ActionExecutedContext context)
{
_logger.LogDebug("[path]" + context.HttpContext.Request.Path);
_logger.LogDebug("[method]" + context.HttpContext.Request.Method);
_logger.LogDebug("[body]"); //log request body, expectation: { "id" : "1234", "title" : "test", "status" : "draft"}
_logger.LogDebug("[statuscode]" + context.HttpContext.Response.StatusCode);
_logger.LogDebug("[response]"); //log response
}
}
}
StreamReaderを使用してリクエストの本文を取得しようとすると、空の文字列のみが取得されます。
StreamReader reader = new StreamReader(context.HttpContext.Request.Body);
string text = reader.ReadToEnd();
コントローラーから[fromBody]によってボディが読み取られたため、ストリームを2回読み取ることができないためですか?もしそうなら、どのようにOnActionExecuted
メソッドでリクエストボディとレスポンスを取得するべきですか?
更新:
この「 ミドルウェアのリクエスト本文をログに記録/読み取るための最良の方法 」スレッドに応じて、以下が機能するはずです。
// 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;
}
}
リクエストボディを2回読み取る SO postで説明されている同様のアプローチ
Update:ReadBodyAsString
での上記のアプローチは、アクションフィルターではなくミドルウェアで使用すると機能します。違いは、アクションフィルターが呼び出されているとき(OnActionExecuting
の場合でも)、ボディストリームが既に読み取られ、[FromBody] model
が入力されていることです。
良いneswは、context.ActionArguments["<model_name>"]
を使用してアクションフィルターで直接モデルを取得できることです。あなたの場合:
public void OnActionExecuted(ActionExecutedContext context)
{
var model = context.ActionArguments["model"] as NoteModel;
}
コントローラーでIActionResultを使用していて、.NETオブジェクトが必要な場合は、次のようなフィルターを作成できます。
public class SampleFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context)
{
if (context.Result is ObjectResult)
{
var objResult = (ObjectResult)context.Result;
}
}
public void OnActionExecuting(ActionExecutingContext context)
{
}
}
OnActionExecutedに到達する時点で、ObjectResultタスクはすでに完了しているため、値を抽出するだけで済みます。 objResult.StatusCodeでStatusCodeを取得することもできます。
コントローラでは、Ok(...)を返すと実際にOkObjectResultなどが作成されます。
特にシリアライズされた結果が必要な場合は、セットの答えがより有効です。