web-dev-qa-db-ja.com

未加工のリクエスト本文にアクセス

ASP.net 5でリクエストの未加工の入力ボディ/ストリームにアクセスしようとしています。以前は、入力ストリームの位置を0にリセットしてメモリストリームに読み込むことができましたが、これを行おうとするとコンテキストから、入力ストリームはnullであるか、エラーをスローします(System.NotSupportedException => "指定されたメソッドはサポートされていません。")。

以下の最初の例では、コントローラーメソッドのパラメーターオブジェクトタイプを動的として宣言すると、コントローラーの未加工のリクエストにアクセスできます。さまざまな理由から、これは解決策ではなく、とにかく認証フィルターで未加工のリクエスト本文にアクセスする必要があります。

この例は機能しますが、妥当な解決策ではありません。

[HttpPost("requestme")]
public string GetRequestBody([FromBody] dynamic body)
{   
    return body.ToString();
}

エラーをスローします:

[HttpPost("requestme")]
public string GetRequestBody()
{
    var m = new MemoryStream();
    Request.Body.CopyTo(m);

    var contentLength = m.Length;

    var b = System.Text.Encoding.UTF8.GetString(m.ToArray());

    return b;
}

エラーをスローします:

[HttpPost("requestme")]
public string GetRequestBody()
{
    Request.Body.Position = 0;
    var input = new StreamReader(Request.Body).ReadToEnd();

    return input;
}

エラーをスローします:

[HttpPost("requestme")]
public string GetRequestBody()
{
    Request.Body.Position = 0;
    var input = new MemoryStream();
    Request.Body.CopyTo(input);

    var inputString = System.Text.Encoding.UTF8.GetString(input.ToArray());

    return inputString;
}

作成しているAPIに送信されるすべてのリクエストの未加工のリクエスト本文にアクセスする必要があります。

どんな助けや指示も大歓迎です!

編集:

これは、リクエストの本文を読みたいコードです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;
using Microsoft.AspNet.Http;

namespace API.Filters
{
    public class CustomAuthorizationAttribute : Attribute, IAuthorizationFilter
    {
        public CustomAuthorizationAttribute()
        { }

        public void OnAuthorization(AuthorizationContext context)
        {
            if (context == null)
                throw new ArgumentNullException("OnAuthorization AuthorizationContext context can not be null.");
            else
            {
                if (this.AuthorizeCore(context.HttpContext) == false)
                {
                    // Do Other Stuff To Check Auth
                }
                else
                {
                    context.Result = new HttpUnauthorizedResult();
                }
            }
        }

        protected virtual bool AuthorizeCore(HttpContext httpContext)
        {
            var result = false;

            using (System.IO.MemoryStream m = new System.IO.MemoryStream())
            {
                try
                {
                    if (httpContext.Request.Body.CanSeek == true)
                        httpContext.Request.Body.Position = 0;

                    httpContext.Request.Body.CopyTo(m);

                    var bodyString = System.Text.Encoding.UTF8.GetString(m.ToArray());

                    return CheckBody(bodyString); // Initial Auth Check returns true/false <-- Not Shown In Code Here on Stack Overflow
                }
                catch (Exception ex)
                {
                    Logger.WriteLine(ex.Message);
                }
            }
                return false;
        }
    }
}

このコードは、CustomAuthorization属性でマークされたコントローラーメソッドが呼び出されたときにアクセスされます。

[Filters.CustomAuthorizationAuthorization]
[HttpPost]
public ActionResult Post([FromBody]UserModel Profile)
{
    // Process Profile
}
11
Badger Dev

最後の3つのスニペットに見られる例外は、IISまたはWebListener。詳細については、SO質問を参照してください: Asp.Net 5で本文を2回読み取る

とは言っても、application/x-www-form-urlencodedを使用している場合にのみ発生することを期待しています。MVCがファイルアップロードのような長いリクエストを含むリクエストストリームの読み取りを開始するのは安全ではないためです。そうでない場合は、おそらくMVCのバグであり、報告する必要があります https://github.com/aspnet/Mvc

回避策については、このSOの回答をご覧ください。context.Request.ReadFormAsyncの使用方法または手動バッファリングの追加方法について説明しています。 Asp.Net 5で本文を2回読み取る

app.Use(next => async context => {
    // Keep the original stream in a separate
    // variable to restore it later if necessary.
    var stream = context.Request.Body;

    // Optimization: don't buffer the request if
    // there was no stream or if it is rewindable.
    if (stream == Stream.Null || stream.CanSeek) {
        await next(context);

        return;
    }

    try {
        using (var buffer = new MemoryStream()) {
            // Copy the request stream to the memory stream.
            await stream.CopyToAsync(buffer);

            // Rewind the memory stream.
            buffer.Position = 0L;

            // Replace the request stream by the memory stream.
            context.Request.Body = buffer;

            // Invoke the rest of the pipeline.
            await next(context);
        }
    }

    finally {
        // Restore the original stream.
        context.Request.Body = stream;
    }
});
7
Kévin Chalet

同じ問題が発生しました。メソッドシグネチャからパラメーターを削除してから、Request.Body思い通りにストリーミングできます。

2
joe

ストリームを巻き戻して読み取れるようにするには、Request.EnableRewind()を呼び出す必要があります。

string bodyAsString;
Request.EnableRewind();
using (var streamReader = new StreamReader(Request.Body, Encoding.UTF8))
{
    bodyAsString = streamReader.ReadToEnd();
}
0
Steve Peirce