質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

Q&A

解決済

1回答

3922閲覧

Content-Typeにform-urlencodedを指定したリクエストにおいて、MVCを用いてControllerBase.Request内のBodyから値を取得したい。

apiu0191

総合スコア5

HTTP

HTTP(Hypertext Transfer Protocol)とはweb上でHTML等のコンテンツを交換するために使われるアプリケーション層の通信プロトコルです。

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

ASP.NET

ASP.NETは動的なWebサイトやWebアプリケーション、そしてWebサービスを構築出来るようにする為、Microsoftによって開発されたウェブアプリケーション開発フレームワークです。

ASP.NET MVC Framework

ASP.NET MVC Frameworkは、MVCパターンをベースとした、マイクロソフトのウェブアプリケーション開発用のフレームワークです。

0グッド

0クリップ

投稿2021/04/12 06:22

編集2021/04/12 07:22

前提・実現したいこと

初めまして。
C#(asp.netcore)を使用したAPIを作成しております。
受け取ったHttpリクエストをForwardする機能を実現したいと思っています。
C#のForwardするメソッド等を検索したところ、見当たらなかったので自力で実装しようと思っております。

その機能を実現する前段として、リクエスト時のBody値が取得できないため、質問した次第です。

補足(2021/04/12)
実現したい内容の「Forward」という言葉が曖昧であるとご指摘をいただけたため、補足させてください。
実現したい内容といたしましては以下となります。

クライアント(指定なし。ブラウザ、外部APIやアプリ等)⇔ ASP.NET Core Web アプリ(質問部)⇔ どこか他のAPIやWEBアプリ

クライアントからどこか他のWEBアプリへ送られるHttpリクエストを仲介するアプリを作成したいと思っております。

意図といたしましては、ヘッダーで指定した宛先(他のWEBアプリ)へのリクエストを時間帯によって接続不可といったような制御を行いたいためです。
あくまで制御を行いたいという目的のため、送られてきたリクエスト情報と同一のリクエスト内容をどこか他のWEBアプリ等に再度リクエストしたいのです。
また、接続不可の際にあらかじめ用意しているエラー画面を表示させたいため、MVCで実装したいと思っております。

------補足ここまで------

初学者かつ、初めての質問となります。
至らない点等ありましたらご指摘いただけると幸いです。

発生している問題・エラーメッセージ

ControllerでHttpリクエストを受け取った際に、ControllerBase.Request内のBodyの値がStreamで取得できると思いますが、リクエスト時の'Content-Type'ヘッダーに'application/x-www-form-urlencoded'を指定すると取得したStreamが空となってしまいます。

Content-Typeに'application/x-www-form-urlencoded'を指定した際にControllerBase.Request内のBodyの値を取得したいというのが本質問の趣旨となります。

'text/plain'等のContent-Typeを設定した際は取得できますので、困っている次第です。

該当のソースコード

以下、コントローラーのリクエスト受取部になります。

C#

1[Route("Test")] 2public class TestController : Controller 3{ 4 private IHttpClientFactory _clientFactory; 5 6 // DI 7 public TestController(IHttpClientFactory clientFactory) 8 { 9 _clientFactory = clientFactory; 10 } 11 12 [HttpPost] 13 public async Task<IActionResult> PostAsync([FromHeader]TestHeader header) 14 { 15 var bodyStr = string.Empty; 16 17 using(StreamReader reader = new StreamReader(Request.Body)) 18 { 19 bodyStr = await reader.ReadToEndAsync().ConfigureAwait(false); 20 } 21 22 return Ok(bodyStr); 23 } 24}

以下、リクエストはPowerShellから投げております。

PowerShell

1#読み取れるリクエスト 2Invoke-WebRequest 'http://localhost:8080/Test' ` 3 -mehtod post ` 4 -Headers @{"Content-type"="text/plain"} ` 5 -body 'hoge' 6 7#以下問題のリクエスト 8Invoke-WebRequest 'http://localhost:8080/Test' ` 9 -mehtod post ` 10 -Headers @{"Content-type"="application/x-www-form-urlencoded"} ` 11 -body 'hoge' # 本来は"aaa=aaa&bbb=bbb"のような値を送りたい。

試したこと

Postメソッドの引数に'[FromForm]'を指定すれば解決するのですが、Bodyの型を指定したくなく、すべてのリクエストに対応したいためあえてStreamを読み込んでおります。

前提に書きました通り、他のContent-Type(text/plain, text/xml, application/json等)で試行しております。その際は文字列は返却できておりました。

またStreamを読み込んだ時点で値が空となりますので、リクエスト返却時ではなくリクエスト受付時の問題と認識しております。

stackoverflowにてForwardを実装している記事を見たのですが、mvcではなくWebHttpRequestを利用したものとなっており、本問題には使えないかなと思い手詰まりとなってしまいました。
該当URL:stackoverflowのリンク

明確な質問とならず申し訳ございません。
ご助力いただけますと幸いです。

補足情報(FW/ツールのバージョンなど)

言語:C#
フレームワーク:.NetCore3.1
実行環境:VisualStudioProfessional2019 Version 16.8.5内のIISExpress

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2021/04/12 06:36 編集

> 受け取ったHttpリクエストをForwardする その Forward というのは具体的に何ですか? クライアント(ブラウザ) ⇔ ASP.NET Core Web アプリ ⇔ どこか他の Web API というような構成で、クライアントからの要求を受けて、「どこか他の Web API」から情報を入手して、それをクライアントに返すというようなことですか? そのあたりを詳しく、ここに書いてあること以外は知り得ない第三者が読んで分かるように書いてください。
apiu0191

2021/04/12 07:25

修正依頼ありがとうございます。 お時間かかってしまい申し訳ございません。 書けることを書いたつもりではありますが、まだ足りていない点がありましたらお手数ですが再度ご指摘いただけますと幸いです。
退会済みユーザー

退会済みユーザー

2021/04/12 11:55 編集

回答欄に移しました
退会済みユーザー

退会済みユーザー

2021/04/13 02:45

質問者さん、回答欄に Body から取得する方法を追記しておきましたので、それに対するフィードバックを書いてください。このスレッドの課題にはすべて答えていると思います。疑問などがなければフィードバックしていただかなくても結構ですが、このスレッドはクローズしてください。
guest

回答1

0

ベストアンサー

「Bodyから値を取得」とのことですが、MVC で受け取ってそれをそのまま「どこか他のAPIやWEBアプリ」に投げるということですか? それは全く普通ではない、少なくとも私の理解のはるか斜め上のことを考えておられるような気がします。時間制限するのが目的とのことですが、それだけならプロキシサーバーでも間に入れるとか、別の手段を考えた方が良いのでは?

【追記】

下の 2021/04/13 10:47 の私のコメントで「後で回答欄に書いておきます」と書いた件です。

コメント欄で紹介した stackoverflow の記事 (URL 下記) の回答を検証してみました。それに書いてあるようにして Body から文字列を取得できます。

How to read ASP.NET Core Response.Body?
https://stackoverflow.com/questions/43403941/how-to-read-asp-net-core-response-body

startup.cs の Configure メソッドにミドルウェアとして以下を登録。Controller では効果は無いようです。

app.Use(async (context, next) => { context.Request.EnableBuffering(); await next(); });

application/x-www-form-urlencoded 形式でデータを POST。下の画像は Fiddler で見た要求ヘッダとボディ。

イメージ説明

アクションメソッドで HttpRequest.Body を取得。Position が末尾になっているので巻き戻してから(Position を 0 に設定してから)StreamReader で読むと Body の文字列を取得できます。。

イメージ説明

デフォルトでは 1 回しか Body から読めないが、アクションメソッドで Body を取得する時点ではすでに読んでしまっているので Body から読めないということのようです。

上のようにすればアクションメソッドで巻き戻してもう一回読むことは可能です。ただし、わざわざそうしているのは "to make the default configuration of request handling as lightweight and performant as possible" ということだそうで、性能上の劣化が生じる可能性はあるかも。

あと、問題とは直接関係ない話ですが、stackoverflow の記事に書いてあるように、using は使わない、取得し終わったら Stream を巻き戻すようにするのがよいかもしれません。

投稿2021/04/12 11:55

編集2021/04/13 02:36
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

apiu0191

2021/04/12 12:51

回答いただきありがとうございます。 やはり普通ではないのですね。 おっしゃる通り実装しようとしていたものはプロキシサーバー相当のものであり、勉強がてらすべてC#で実装しようとしておりました。 ご回答いただきました通り、別の方法でアプローチしたいと思います。 後学のためご存じでしたらご教授いただきたいのですが、今回問題であるControllerBase.Requestプロパティはクライアントからのリクエストを保持するという認識は誤りなのでしょうか。送った値が消えるというのがとても気持ち悪く。 ご回答いただけたら幸いです。
退会済みユーザー

退会済みユーザー

2021/04/12 13:14

> 今回問題であるControllerBase.Requestプロパティはクライアントからのリクエストを保持するという認識は誤り ボディを保持すると言ってますか? 誤りかどうかは分かりませんが、私自身はそういうことは考えたこともないし、そもそもそういう目的で使えるものではないし、できたとしても意味がないと思っています。
退会済みユーザー

退会済みユーザー

2021/04/13 01:47

上の私のコメントの stackoverflow の記事の回答を検証してみました。それに書いてあるようにして Body から文字列を取得できました。後で回答欄に書いておきます。 デフォルトでは 1 回しか Body から読めないが、アクションメソッドで Body を取得する時点ではすでに読んでしまっているのでダメということのようです。記事のようにすればアクションメソッドで rewind してもう一回読むことは可能です。ただし、わざわざそうしているのは "to make the default configuration of request handling as lightweight and performant as possible" ということだそうで、性能上の劣化が生じる可能性はあるかも。
apiu0191

2021/04/13 12:32

検証までしていただきましてありがとうございます。 プロパティにそれらしい値があったので使用しようと思ったのですが、本来の用途とは違ったのですね。 また、実装次第でとれると分かりすっきりいたしました。 使用しようとは思いませんが、検証いただいた結果を踏まえて再度実装してみたいと思います。 この度はありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問