[解決済み] ASP.NET CoreのResponse.Bodyを読み取るには?
質問
私は
Response.Body
プロパティを取得するのに苦労しており、私が特定できた唯一の解決策は、最適とは言えないようです。 この解決策では
Response.Body
を
MemoryStream
を使いながら、ストリームを文字列変数に読み込んで、クライアントに送信する前にスワップバックしています。 以下の例では、ストリームを文字列変数に読み込む際に
Response.Body
の値をカスタムミドルウェアクラスで使用しています。
Response.Body
は
セット
は、何らかの理由で ASP.NET Core のみのプロパティですか? 私が見落としているだけなのか、それとも見落とし/バグ/設計上の問題なのでしょうか? を読み取る良い方法はありますか?
Response.Body
?
現在の(最適でない)解決策です。
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
using (var swapStream = new MemoryStream())
{
var originalResponseBody = context.Response.Body;
context.Response.Body = swapStream;
await _next(context);
swapStream.Seek(0, SeekOrigin.Begin);
string responseBody = new StreamReader(swapStream).ReadToEnd();
swapStream.Seek(0, SeekOrigin.Begin);
await swapStream.CopyToAsync(originalResponseBody);
context.Response.Body = originalResponseBody;
}
}
}
EnableRewind()を用いた解決策を試みました。
これは
Request.Body
に対してのみ動作し
Response.Body
. この結果、空の文字列を
Response.Body
から空文字列を読み取ることになります。
スタートアップ.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifeTime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
app.UseMyMiddleWare();
app.UseMvc();
// Dispose of Autofac container on application stop
appLifeTime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
MyMiddleWare.cs
public class MyMiddleWare
{
private readonly RequestDelegate _next;
public MyMiddleWare(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
await _next(context);
string responseBody = new StreamReader(context.Request.Body).ReadToEnd(); //responseBody is ""
context.Request.Body.Position = 0;
}
}
どのように解決するのですか?
最初の回答で、私は質問を完全に読み間違えていて、投稿者が
Request.Body
をどのように読むかを尋ねていたのです。
Response.Body
. 私は歴史を保存するために元の答えを残していますが、一度正しく読んだらどのように答えるかを示すために更新しています。
元の回答
複数回の読み込みをサポートするバッファードストリームが必要な場合、以下のように設定する必要があります。
context.Request.EnableRewind()
理想的には、ミドルウェアの早い段階で、何かがボディを読む必要がある前にこれを行うことです。
ですから、例えば次のようなコードを
Configure
メソッドの冒頭に記述します。
app.Use(async (context, next) => {
context.Request.EnableRewind();
await next();
});
巻き戻しを有効にする前に
Request.Body
に関連付けられたストリームは、2 度目のシークや読み込みをサポートしない、 前進のみのストリームです。 これは、リクエスト処理のデフォルトの構成を可能な限り軽量で高性能なものにするために行われました。 しかし、一旦巻き戻しを有効にすると、ストリームは複数回のシークと読み込みをサポートするストリームにアップグレードされます。 を呼び出す直前と直後にブレークポイントを設定することで、 この "upgrade" を観察することができます。
EnableRewind
の呼び出しの直前と直後にブレークポイントを設定し
Request.Body
のプロパティを観察します。 ですから、例えば
Request.Body.CanSeek
は
false
から
true
.
更新
: ASP.NET Core 2.1での開始について
Request.EnableBuffering()
をアップグレードすることができます。
Request.Body
を
FileBufferingReadStream
と同じように
Request.EnableRewind()
であり
Request.EnableBuffering()
は内部ではなく、パブリックな名前空間にあるので、EnableRewind() よりも優先されるべきです。 (指摘してくれた@ArjanEinbuに感謝します)
それから、ボディストリームを読むために、例えば次のようにすることができます。
string bodyContent = new StreamReader(Request.Body).ReadToEnd();
をラップしないで
StreamReader
さもないと、using ブロックの終了時に基礎となるボディストリームが閉じられ、リクエストライフサイクルの後半にあるコードはボディを読むことができなくなります。
また、念のため、ボディの内容を読み取る上記のコード行の後に、ボディのストリーム位置を 0 に戻すこのコード行を続けるのもよい考えかもしれません。
request.Body.Position = 0;
そうすれば、リクエストのライフサイクルの後半にあるどんなコードも、まだ読まれていないような状態のrequest.Bodyを見つけることができるようになります。
更新された回答
最初に質問を読み間違えてしまい、申し訳ありません。関連するストリームをバッファード ストリームにアップグレードするコンセプトは、まだ適用されます。しかし、手動で行う必要があり、.Net Core の内蔵機能で
EnableRewind()
でリクエスト ストリームを読み込んだ後に再読み込みできるような、一度書き込んだレスポンス ストリームを読み込める .Net Core の組み込み機能を知りません。
あなたの "hacky" アプローチは、おそらく完全に適切です。基本的に、シークできないストリームをシークできるストリームに変換しているのです。 結局のところ
Response.Body
ストリームは、バッファリングされシークをサポートするストリームと交換されなければなりません。 これを行うためのミドルウェアの別の方法を紹介しますが、あなたのアプローチと非常によく似ていることにお気づきでしょう。 しかし、私は元のストリームをResponse.Body
に戻し、さらに
Position
プロパティではなく、ストリームの
Seek
メソッドよりもストリームのプロパティの方が、構文が少し単純なので、効果はあなたのアプローチと変わりません。
public class ResponseRewindMiddleware
{
private readonly RequestDelegate next;
public ResponseRewindMiddleware(RequestDelegate next) {
this.next = next;
}
public async Task Invoke(HttpContext context) {
Stream originalBody = context.Response.Body;
try {
using (var memStream = new MemoryStream()) {
context.Response.Body = memStream;
await next(context);
memStream.Position = 0;
string responseBody = new StreamReader(memStream).ReadToEnd();
memStream.Position = 0;
await memStream.CopyToAsync(originalBody);
}
} finally {
context.Response.Body = originalBody;
}
}
}
関連
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み】取り消せないメンバはメソッドのように使えない?
-
[解決済み] enumを列挙するには
-
[解決済み] intをenumにキャストするにはどうすればよいですか?
-
[解決済み] 辞書を繰り返し使用するには?
-
[解決済み] 乱数(int)を生成する方法を教えてください。
-
[解決済み] ASP.NET CoreでカスタムのAuthorizeAttributeを作成する方法は?
-
[解決済み] ConfigureServices内からASP.NET Core DIでインスタンスを解決する
-
[解決済み】ASP.NET COREでクライアントのIPアドレスを取得する方法とは?
-
[解決済み】ASP.NET Core IConfigurationを使ったJson配列の取得
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】スクリプトクラスが見つからないので、スクリプトコンポーネントを追加できない?
-
[解決済み】C#で四捨五入する方法
-
[解決済み】統合マネージドパイプラインモードで適用されないASP.NETの設定が検出された
-
[解決済み] エンティティタイプ <type> は、現在のコンテキストのモデルの一部ではありません。
-
[解決済み】ソケットのアドレス(プロトコル/ネットワークアドレス/ポート)は、通常1つしか使用できない?
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み】"指定されたパスのフォーマットはサポートされていません。"
-
[解決済み】URLから画像をダウンロードする方法
-
[解決済み】IntPtrとは一体何なのか?
-
[解決済み】データが存在しないのに読み込もうとする試みが無効である