web-dev-qa-db-ja.com

asp.netコアwebapiコントローラーでリクエスト本文を読み取る方法

OnActionExecutingメソッドでリクエストの本文を読み取ろうとしていますが、私は常にnullを取得しています。

var request = context.HttpContext.Request;
var stream = new StreamReader(request.Body);
var body = stream.ReadToEnd();

ストリームの位置を明示的に0に設定しようとしましたが、それも機能しませんでした。これはASP.NET COREなので、物事は少し違うと思います。ここでは、古いwebapiバージョンを参照するすべてのサンプルを見ることができます。
これを行う他の方法はありますか?

55
Kasun Koswattha

リクエスト本文を巻き戻すことができるようにするために、@ Jeanの答えは私がうまくいくと思われる解決策を思い付くのに役立ちました。現在、これをグローバル例外ハンドラミドルウェアに使用していますが、原理は同じです。

(デコレータの代わりに)基本的にリクエスト本文の巻き戻しを可能にするミドルウェアを作成しました。

using Microsoft.AspNetCore.Http.Internal;
[...]
public class EnableRequestRewindMiddleware
{
    private readonly RequestDelegate _next;

    public EnableRequestRewindMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        context.Request.EnableRewind();
        await _next(context);
    }
}

public static class EnableRequestRewindExtension
{
    public static IApplicationBuilder UseEnableRequestRewind(this IApplicationBuilder builder)
    {
        return builder.UseMiddleware<EnableRequestRewindMiddleware>();
    }
}

これは、次のようにStartup.csで使用できます。

[...]
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    [...]
    app.UseEnableRequestRewind();
    [...]
}

このアプローチを使用して、リクエスト本文ストリームを正常に巻き戻すことができました。

14
SaoBiz

より明確なソリューションは、ASP.Net Core 2.1で動作します。

フィルタークラス

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.AspNetCore.Mvc.Filters;

public class ReadableBodyStreamAttribute : AuthorizeAttribute, IAuthorizationFilter
{
    public void OnAuthorization(AuthorizationFilterContext context)
    {
        context.HttpContext.Request.EnableRewind();
    }
}

コントローラー内

[HttpPost]
[ReadableBodyStream]
public string SomePostMethod()
{
    using (StreamReader stream = new StreamReader(HttpContext.Request.Body))
    {
        string body = stream.ReadToEnd();
        // body = "param=somevalue&param2=someothervalue"
    }
}
6
Andriod

このルートを使用する場合、IHttpContextAccessorメソッドは機能します。

TLDR;

  • IHttpContextAccessorを注入します

  • 巻き戻し-HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);

  • 読み取り-System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body); JObject asObj = JObject.Parse(sr.ReadToEnd());

More-確実にコンパイルする必要のあるアイテムの例。使用可能なIHttpContextAccessor。リクエストの本文を読み込もうとすると、最初に戻ってシークする必要があるという回答が正しく指摘されています。これを確認するのに役立つ、リクエストボディストリームのCanSeekPositionプロパティ。

。NET Core DI Docs

// First -- Make the accessor DI available
//
// Add an IHttpContextAccessor to your ConfigureServices method, found by default
// in your Startup.cs file:
// Extraneous junk removed for some brevity:
public void ConfigureServices(IServiceCollection services)
{
    // Typical items found in ConfigureServices:
    services.AddMvc(config => { config.Filters.Add(typeof(ExceptionFilterAttribute)); });
    // ...

    // Add or ensure that an IHttpContextAccessor is available within your Dependency Injection container
    services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
}

// Second -- Inject the accessor
//
// Elsewhere in the constructor of a class in which you want
// to access the incoming Http request, typically 
// in a controller class of yours:
public class MyResourceController : Controller
{
    public ILogger<PricesController> Logger { get; }
    public IHttpContextAccessor HttpContextAccessor { get; }

    public CommandController(
        ILogger<CommandController> logger,
        IHttpContextAccessor httpContextAccessor)
    {
        Logger = logger;
        HttpContextAccessor = httpContextAccessor;
    }

    // ...

    // Lastly -- a typical use 
    [Route("command/resource-a/{id}")]
    [HttpPut]
    public ObjectResult PutUpdate([FromRoute] string id, [FromBody] ModelObject requestModel)
    {
        if (HttpContextAccessor.HttpContext.Request.Body.CanSeek)
        {
            HttpContextAccessor.HttpContext.Request.Body.Seek(0, System.IO.SeekOrigin.Begin);
            System.IO.StreamReader sr = new System.IO.StreamReader(HttpContextAccessor.HttpContext.Request.Body);
            JObject asObj = JObject.Parse(sr.ReadToEnd());

            var keyVal = asObj.ContainsKey("key-a");
        }
    }
}    
1
Randy Larson