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

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

ただいまの
回答率

90.62%

  • HTTP

    529questions

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

  • POST

    225questions

    POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

  • Windows Forms

    98questions

    Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

  • ASP.NET Web API

    10questions

    ASP.NET Web APIはブラウザやモバイル機器のようなクライアント向けのHTTPサービスを構築するフレームワークです。Microsoft .NET Frameworkがベースになっており、RESTfulサービスを構築するには理想的です。

ASP.NET WebApi を HttpClient の PostAsyncメソッドで呼び出し

解決済

回答 2

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 6,028

skippanther

score 5

ASP.NET WebApiのPost処理(C#)を、Windowsフォームアプリ(C#)から、HttpClient の PostAsyncメソッドを使って呼ぼうとしているのですが、どうもうまくいきません。

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

以下が返されて、対象のPostメソッドは呼ばれません。
      The requested resource does not support http method 'GET'.

ソースコード

まず、WebApiの記述です。
    [RoutePrefix("api/GateApi")]
    public class GateApiController : ApiController
    {
   // パラメータ用のクラス定義
        public class para
        {
            public int GateNo { get; set; }
        }

   // Post処理
        [Route("TestPost")]
        [HttpPost]
        public string TestPost(para data)
        {
            return "Post呼ばれたよ";
        }
    }
試しに、同じサイト内のページに埋め込んだ、以下のJavascriptでPOSTしてみましたが、これは正常に処理されます。
値も正常に渡るし、戻り値も返っています。
    function testPost() {
        $.ajax(
        "/api/GateApi/TestPost/",
        {
            type: "POST",
            data: { GateNo: 333 },
            success: function (data) {
                alert(data);
            },
            error: function (e) {
                alert("エラー");
            }
        });
    }
以下が、Windows Formのソースの一部です。
ボタンをクリックするとレスポンスがラベルに表示されます。
        private void button_Click(object sender, EventArgs e)
        {
            PostWebPageAsync();
        }

        private async void PostWebPageAsync()
        {
            this.label1.Text = "Post取得中";

            string uri = "http://localhost:12345/api/GateApi/TestPost/";

            // "Message":"The requested resource does not support http method 'GET'. が返されます。
            this.label1.Text = await Post(uri, 
                        new Dictionary<string, string> {{ "GateNo", "123" }});
        }

        // Post呼び出し処理
        private async Task<string> Post(string url, Dictionary<string, string> param)
        {
            string result = "";

            try
            {
                HttpClient httpClient = new HttpClient();
                httpClient.MaxResponseContentBufferSize = int.MaxValue;
                HttpContent content = new FormUrlEncodedContent(param);
                var response = await httpClient.PostAsync(url, content);
                String text = await response.Content.ReadAsStringAsync();

                result = text;
            }
            catch (Exception Err)
            {
                result = "ERROR: " + Err.Message;
            }

            return result;
        }
例外は発生していないようですが、ラベルには、
   "Message":"The requested resource does not support http method 'GET'. 
が表示されます。
 response.StatusCode は  MethodNotAllowed が返されます。

何か、ヘッダなど必要な情報があるんでしょうが、よくわかりません。

補足情報

サーバー側もクライアント側も、.NETのバージョンは 4.5.1

IISのログの抜粋です
---------------------------------------------------------------------
■うまくいくとき → ChromeのRest Client で呼び出し
2015-10-13 03:06:50 ::1 POST /api/GateApi/TestPost - 12345 - ::1 Mozilla/5.0+(Windows+NT+6.1;+WOW64)+AppleWebKit/537.36+(KHTML,+like+Gecko)+Chrome/45.0.2454.101+Safari/537.36 - 200 0 0 2620

■うまくいかないとき → WindowsForm で呼び出し
2015-10-13 03:09:21 ::1 POST /api/GateApi/TestPost/ - 12345 - ::1 - - 302 0 0 13
2015-10-13 03:09:21 ::1 GET /api/GateApi/TestPost/ AspxAutoDetectCookieSupport=1 53903 - ::1 - - 405 0 0 266
---------------------------------------------------------------------
上記ログ(うまくいかないほう)とソースの対応を調べたのですが、
  1行目: var response = await httpClient.PostAsync(url, content);  →  POST  
  2行目: String text = await response.Content.ReadAsStringAsync(); → GET
ということでした。
1行目の処理で、エラーが帰りそのため2行目のGETがうまくいかないということのようです。

1行目POST時の戻り値の内容を一部抜粋
---------------------------------------------------------------------
    IsSuccessStatusCode: false
    ReasonPhrase: "Method Not Allowed"
    RequestMessage: {Method: POST, RequestUri: 'http://localhost:12345/api/GateApi/TestPost/?AspxAutoDetectCookieSupport=1', Version: 1.1, Content: System.Net.Http.FormUrlEncodedContent, Headers:
{
  Content-Type: application/x-www-form-urlencoded
  Content-Length: 10
}}
---------------------------------------------------------------------

[その後の調査]

eripong さん

貴重な情報ありがとうございます。
セッションIDの受渡のために、クッキーを使うかどうかの設定の話だと思うんですが、私の知識では、いまいち細かいしくみがわかりません。ただ、これを機に多少とも勉強させていただきます。

最低限の私の知識で理解できる範囲で、以下いろいろやってみました。

■まず、cookieless の設定による実行結果です。(○がPOST成功)
   × UseUri
   ○ UseCookies
   × AutoDetect
   ○ UseDeviceProfile

おっしゃるように、UseCookiesだとうまくいくようで、UseDeviceProfileでも大丈夫みたいです。

つまり、設定としては、
  • <sessionState mode="Off" />
  • <sessionState cookieless="UseCookies"/>
  • <sessionState cookieless="UseDeviceProfile"/>
のいずれかだとうまくいくということになります。


■次に、Chromeのクッキーの状態を見てみました。
イメージ説明AspxAutoDetectCookieSupportの値が1として保存されているようです。

試しに、これを削除して、JavascriptやREST ClientツールでGETしてみると、見事にエラーになります。
ただ、このときに再度AspxAutoDetectCookieSupport=1 が保存され、2回目以降はエラーにならないようです。

■REST Client でのPOST実行結果
各処理のリクエストの内容を細かく調べる方法がわからないので、とりあえず情報として載せておきます。

以下は、REST Client でPOSTに失敗した時の状況です。
イメージ説明POST時に、AspxAutoDetectCookieSupport=1にリダイレクトしてるということでしょうか。その結果「405 Method Not Allowed」が返されています。そして、その後、GETしようとしてエラー返されているようです。

最後に、REST Client でPOSTに成功した時の状況です。
イメージ説明
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • tkow

    2015/10/11 16:12 編集

    IISが使用しているポート番号が正しく設定されていますでしょうか? IISはデフォルトでは通常の80や8080ポートではなく乱数であいているポートを使用していたと思うのでそうなっていないか確認してみてください。

    キャンセル

  • skippanther

    2015/10/13 08:51

    とりあえずはVSのデバッグモードで実行しています。つまり、実行環境としてはデバッグ時に起動されるドメインとポート番号を指定しているということです。 ためしに、Chromeの拡張ツール Advanced Rest Clientで、ちゃんとこのメソッドが呼ばれることを確認しました。

    キャンセル

  • skippanther

    2015/10/13 09:25

    ちなみに、ポートが間違っている場合は、 "ERROR: 無効な URI: 無効なポートが指定されています。" が返されました。

    キャンセル

  • eripong

    2015/10/13 11:48

    IISのログはどうなっていますか?POSTできているかや、うまく行く場合との差分の確認はできませんか?

    キャンセル

回答 2

checkベストアンサー

+1

調べ切れていませんが、セッションの実現のための仕組みのようです。
rest - asp.net web api and aspxautodetectcookiesupport - Stack Overflow
では、セッションをoffにする事で現象が解消したとありますが、
セッションは使用されているでしょうか?offにすることはできますか?

 問題が発生する仕組みについて

まず、POSTした後に、クエリストリングにAspxAutoDetectCookieSupport=1を付けて
GETしているようなのは、sessionStateのcookielessがAutoDetectの場合に発生する様です。

今回の場合、最初のPOSTは、以下に述べるセッションのための処理を行うために、
ASP.NETによって処理され、二番目に来るGETを関数TestPostに振り分けようとして、
POSTにしか対応していないのでエラー、ということだと思います。

同じサイト内のページに埋め込んだJavascriptやChromeのRest Clientでうまくいったのは、
既に今回のアプリケーションにアクセスしており、リクエストにクッキーが載っていたためではないかと
予想しています。
(確認できれば、確認していただければと思います。)

セッションIDをクッキーに保存するかURLに埋め込むかを決めるために、
クッキーが使えるクライアントか、そうでないかを判定するために、
AspxAutoDetectCookieSupport=1にリダイレクトしています。
以下のページに詳しい判定方法が載っています。
Understand How the ASP.NET Cookieless Feature Works

また、SessionStateSection.Cookieless プロパティを見ると、
cookielessのデフォルトはAutoDetectの様なので、こちらをUseCookiesにすれば、
現象が解消する可能性はあります。
その場合、HttpClientでCookieが有効になる様にする必要があります。
ただ、UseCookiesにすればリダイレクトがなくなるかどうかについては、
今のところ有効な資料を見つけられておらず、また検証する環境もないので、
うまく行くかは未知数です。

UseUriは、セッションID付きのURLへのリダイレクトを伴うと思いますので、
いずれにしてもリダイレクトした場合にGETリクエストになって同じエラーになるかと思います。
(こちらも検証していないので予想です。)

セッション自体をなくしてしまえば、これらの仕組みが動かなくなるので、
セッションをoffにすれば改善するだろうと思って回答しました。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/13 13:36

    もう少しまとまったら、回答を更新します。

    キャンセル

  • 2015/10/13 14:38 編集

    ありがとうございました。
    Web.Config で、セッションをOFFにすることで解決しました。
    <sessionState mode="Off" />
    よく理由がわからないんですが、、、
    ※同じ HttpClient のメソッドの GetAsync() はセッションに関係なくうま
    くいきます。

    キャンセル

  • 2015/10/13 17:28 編集

    セッションoffでうまくいく理由を追記しました。

    HttpClient のメソッドの GetAsync() がうまくいく、
    というのは、呼び出し先のメソッドをHttpPostでなくHttpGetにした
    ということでしょうか?
    リダイレクトされるとGETリクエストになるので、
    呼び出し先のメソッドがGETリクエストに対応していれば、
    うまく動くだろうと思います。

    キャンセル

  • 2015/10/14 18:22

    色々な検証結果の共有、ありがとうございます。
    予想した流れは大体合っていたようで、良かったです。

    > POST時に、AspxAutoDetectCookieSupport=1にリダイレクトしてるということでしょうか。その結果「405 Method Not Allowed」が返されています。そして、その後、GETしようとしてエラー返されているようです。

    この部分は、ちょっと違うと思います。

    POST時に、302応答が返り、リダイレクト先として、AspxAutoDetectCookieSupport=1が指定される。その後、リダイレクト先へのGETを行い、405応答が返る。

    という流れと思います。

    キャンセル

  • 2015/10/14 21:43

    ご指摘ありがとうございます。
    Webの開発は難しいですね。後々のことを考えると、やはりその都度ちきんと検証していないとなかなかスキルが身につかないですね。

    キャンセル

  • 2015/10/15 15:41

    そうですね。
    原理を理解することと、その理解を検証して確認することで、
    身になる気がします。

    > JavascriptやREST ClientツールでGETしてみると、見事にエラーになります。
    の部分は、POSTでもエラーにならないですか?

    キャンセル

  • 2015/10/19 19:53

    すいません
    レスを見逃していました。
    >> JavascriptやREST ClientツールでGETしてみると、見事にエラーになります。
    の部分は、POSTでもエラーにならないですか?

    エラーになるのは、GET、POST 両方です。

    キャンセル

  • 2015/10/19 20:42

    返信ありがとうございます。
    両方であれば、納得です。

    キャンセル

+1

恐らくですが,POSTの引数の型解釈が出来なくてエラーが出ているのが根本的な原因ではないでしょうか。

ここに対策として,引数に[FromBody]属性を書く方法が載っています。javascriptの対処の方は以下の例ではクエリパラメータでリクエストを渡しているので今回は参考にしなくても大丈夫です。

以下の資料によれば,POSTメソッドの引数指定は文字列から変換できる型のみに限定されるようです。恐らく独自定義クラスだとキャストを定義する,インターフェースを継承する必要があるのではないかと思います。[FromBody]属性を付けるとリクエストを引数をbodyから受け取るようにして空のリクエストも引数有りにするのだと思います。(javascriptで正しく動いていることからURIに引数がないから怒られている可能性もあります。)

また,POSTリクエストをDictionary型で渡しているところも怪しいので文字列に変換してからリクエストしてみてはいかがでしょうか。

WebApi2 Requested resource does not support post

何が解決策になるか解りませんがいずれかの方法で解決するように思います。

追記:session offで解決するとのことなのでもしかすると,ASP.NETではリクエストヘッダかボディにデフォルトでセッションに使うパラメータが入っていて,それが引数の型で解釈できないせいでエラーが出ている可能性もあります。

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/10/13 16:26

    たしかに「FromBody をつけろ」というのは、あちこちのサイトにありましたね。
    public string TestPost([FromBody] para data)
    でも、これをつけても効果はありませんでした。

    PostAsyncの第2引数についても、渡し方をいろいろ変えてみましたが、やり方が悪いのか、状況は変わらないようです。

    時間があるときにさらに調べてみるつもりではいますが、今回は Session を OFF にすることで、解決とさせていただきます。

    キャンセル

  • 2015/10/14 01:50 編集

    いろいろ試していただいたあとなのですね。
    こちらも色々勉強になりました。
    URIに付加されてることからやはりcookieを処理するメソッドがないのが原因で,デフォルトでcookieが付加されてしまうのが原因の可能性が高そうです。

    キャンセル

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

  • ただいまの回答率 90.62%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • HTTP

    529questions

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

  • POST

    225questions

    POSTはHTTPプロトコルのリクエストメソッドです。ファイルをアップロードしたときや入力フォームが送信されたときなど、クライアントがデータをサーバに送る際に利用されます。

  • Windows Forms

    98questions

    Windows Forms(WinForms)はMicrosoft .NET フレームワークに含まれる視覚的なアプリケーションのプログラミングインターフェイス(API)です。WinFormsは管理されているコードの既存のWindowsのAPIをラップすることで元のMicrosoft Windowsのインターフェイスのエレメントにアクセスすることができます。

  • ASP.NET Web API

    10questions

    ASP.NET Web APIはブラウザやモバイル機器のようなクライアント向けのHTTPサービスを構築するフレームワークです。Microsoft .NET Frameworkがベースになっており、RESTfulサービスを構築するには理想的です。