Web APIコントローラーの複数のHttpPostメソッド


126

MVC4 Web APIプロジェクトを使い始めましたHttpPost。複数のメソッドを持つコントローラーがあります。コントローラは次のようになります。

コントローラ

public class VTRoutingController : ApiController
{
    [HttpPost]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

以下MyRequestTemplateは、リクエストを介して送信されるJsonの処理を担当するテンプレートクラスを表します。

エラー:

Fiddler forを使用してリクエストを行うhttp://localhost:52370/api/VTRouting/TSPRoutehttp://localhost:52370/api/VTRouting/Route 、エラーが発生します。

リクエストに一致する複数のアクションが見つかりました

上記の方法のいずれかを削除すると、正常に動作します。

Global.asax

でデフォルトのルーティングテーブルを変更しようとしましたがglobal.asax、それでもエラーが発生します。global.asaxでルートを定義するときに問題があると思います。これが、global.asaxで行っていることです。

public static void RegisterRoutes(RouteCollection routes)
{
    routes.MapHttpRoute(
        name: "MyTSPRoute",
        routeTemplate: "api/VTRouting/TSPRoute",
        defaults: new { }
    );

    routes.MapHttpRoute(
        name: "MyRoute",
        routeTemplate: "api/VTRouting/Route",
        defaults: new { action="Route" }
    );
}

POSTを使用してFiddlerでリクエストを作成し、MyRequestTemplateのRequestBodyにjsonを渡します。

回答:


143

1つのコントローラーで複数のアクションを持つことができます。

そのためには、次の2つのことを行う必要があります。

  • 最初のActionNameような属性でアクションを飾る

     [ActionName("route")]
     public class VTRoutingController : ApiController
     {
       [ActionName("route")]
       public MyResult PostRoute(MyRequestTemplate routingRequestTemplate)
       {
         return null;
       }
    
      [ActionName("tspRoute")]
      public MyResult PostTSPRoute(MyRequestTemplate routingRequestTemplate)
      {
         return null;
      }
    }
  • 次に、次のルートをWebApiConfigファイルに定義します。

    // Controller Only
    // To handle routes like `/api/VTRouting`
    config.Routes.MapHttpRoute(
        name: "ControllerOnly",
        routeTemplate: "api/{controller}"               
    );
    
    
    // Controller with ID
    // To handle routes like `/api/VTRouting/1`
    config.Routes.MapHttpRoute(
        name: "ControllerAndId",
        routeTemplate: "api/{controller}/{id}",
        defaults: null,
        constraints: new { id = @"^\d+$" } // Only integers 
    );
    
    // Controllers with Actions
    // To handle routes like `/api/VTRouting/route`
    config.Routes.MapHttpRoute(
        name: "ControllerAndAction",
        routeTemplate: "api/{controller}/{action}"
    );

IDのタイプに制限を設定したくない場合はどうなりますか?意味:文字列IDを受け入れるにはどうすればよいですか?
frapontillo 2013

5
@frapontillo:Idは整数でなければならないため、ルート名と区別されるため、ルーティングエンジンはIDではなくアクション名として扱います。文字列としてIDが必要な場合は、アクションを作成できます。
Asif Mushtaq 2013

代わりに属性ルーティングを使用します。このようにWebApiConfigで複数のルートを使用する必要はありません。このリンクをチェックアウト: docs.microsoft.com/en-us/aspnet/web-api/overview/...
リッチ

このように追加すると、エラーが発生します------------ namespace ImageDownloadApplication.Controllers {public class FrontModel {public string skus {get; セットする; }} [ActionName( "ProductController")] public class ProductController:ApiController {// GET:api / NewCotroller public IEnumerable <string> Get(){return new string [] {"value1"、 "value2"}; }
Umashankar

41

問題のはるかに良い解決策は、Routeアノテーションを使用してメソッドのルートを指定できるようにすることです。

[RoutePrefix("api/VTRouting")]
public class VTRoutingController : ApiController
{
    [HttpPost]
    [Route("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost]
    [Route("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

Routeはどの名前空間にありますか?MVC4を使用していますが、Routeが認識されません。
eaglei22


はい、これはそれが行くべき道です。ありがとう。
ニューマン'19 / 07/19

1
何らかの理由で、これを機能させることができません。これはまさに私がすでにやっていたことです。
オリゴフレン2016年

2
どの呼び出しによりたいURL見た目RouteTSPRoute
Si8 2017年

27

使用する:

routes.MapHttpRoute(
    name: "DefaultApi",
    routeTemplate: "api/{controller}/{action}/{id}",
    defaults: new { id = RouteParameter.Optional }
);

RESTfulアプローチではなくなりましたが、次のように(動詞に基づいてWeb APIが自動的にアクションを決定するのではなく)名前でアクションを呼び出すことができるようになりました。

[POST] /api/VTRouting/TSPRoute

[POST] /api/VTRouting/Route

一般的な考えに反して、このアプローチには何も問題がなく、Web APIを悪用していません。Web APIのすべての優れた機能(委任ハンドラー、コンテンツネゴシエーション、メディアタイプフォーマッターなど)を引き続き利用できます。RESTfulなアプローチを取りやめるだけです。


1
答えてくれてありがとう、それでも同じエラーが出ます。
ハビブ2012

それは不可能であり、アプリで他の何かを誤って設定する必要があります。ルート設定全体を表示できますか?また、コントローラーアクションを正確にどのよう呼び出していますか?
Filip W

ルートの設定全体はglobal.asaxにあります。質問にその部分を投稿します。リクエストを行うために、Fiddler-> Compose->を使用し、操作としてPostを選択しています
Habib

他のすべてのルート定義を削除して、投稿したものをそのままにしてください。次に、1つのコントローラー内にある両方のPOSTアクションを簡単に呼び出すことができます(以前のMVCアプローチと同じ)
Filip W

1
フィリップは、私はあなたのプロジェクトを作成するために使用しているテンプレートmvc4またはVisual Studio 2012 RCと、.NET Frameworkの4.5を使用しています、あなたは完璧に働いている
ハビブ

13

Web APIエンドポイント(コントローラー)は、get / post / put / delete動詞を受け入れる単一のリソースです。通常のMVCコントローラーではありません

必然的に、送信するパラメーターを受け入れるHttpPostメソッドは1つ/api/VTRoutingだけです。[http]で装飾している限り、関数名は重要はありません。しかし、私は試したことがない。

編集: これは機能しません。解決においては、型にモデルバインドしようとするのではなく、パラメータの数が原因のようです。

関数をオーバーロードして、さまざまなパラメーターを受け入れることができます。あなたがそれをあなたのやり方で宣言したが、メソッドに異なる(互換性のない)パラメータを使用するなら、あなたは大丈夫だと確信しています。パラメータが同じである場合、モデルバインディングがどちらを意味するのかわからないので、あなたは運が悪いです。

[HttpPost]
public MyResult Route(MyRequestTemplate routingRequestTemplate) {...}

[HttpPost]
public MyResult TSPRoute(MyOtherTemplate routingRequestTemplate) {...}

この部分は動作します

あなたが新しいものを作成するときに彼らが与えるデフォルトのテンプレートはこれをかなり明白にします、そして私はあなたがこの慣習に固執するべきだと言います:

public class ValuesController : ApiController
{
    // GET is overloaded here.  one method takes a param, the other not.
    // GET api/values  
    public IEnumerable<string> Get() { .. return new string[] ... }
    // GET api/values/5
    public string Get(int id) { return "hi there"; }

    // POST api/values (OVERLOADED)
    public void Post(string value) { ... }
    public void Post(string value, string anotherValue) { ... }
    // PUT api/values/5
    public void Put(int id, string value) {}
    // DELETE api/values/5
    public void Delete(int id) {}
}

多くのことを行う1つのクラスを作成する場合、ajaxを使用するために、標準のコントローラー/アクションパターンを使用しない大きな理由はありません。唯一の本当の違いは、メソッドシグネチャがそれほどきれいではなく、Json( returnValue)返す前にラップする必要があることです。

編集:

単純型を使用するときに標準テンプレート(インクルードするように編集)を使用すると、オーバーロードは正常に機能します。私は別の方法で行ってテストしましたが、署名が異なる2つのカスタムオブジェクトを使用しました。それを機能させることはできませんでした。

  • 複雑なオブジェクトとのバインドは「ディープ」に見えないので、それはノーゴーです
  • クエリ文字列に追加のパラメーターを渡すことでこれを回避できます
  • 利用可能なオプションに与えることができるよりも良い記事

この場合、これは私にとってうまくいきました。テストのみの例外。

public class NerdyController : ApiController
{
    public void Post(string type, Obj o) { 
        throw new Exception("Type=" + type + ", o.Name=" + o.Name ); 
    }
}

public class Obj {
    public string Name { get; set; }
    public string Age { get; set; }
}

そしてコンソールからこのように呼ばれます:

$.post("/api/Nerdy?type=white", { 'Name':'Slim', 'Age':'21' } )

パラメータタイプを変更してみましたが、コントローラで単一のPostメソッドしか許可されていないようです。お返事ありがとう
ハビブ2012

あなたはオーバーロードすることができるので、それはそれを見つけるためにモデルバインディングを試みると仮定しました。ただし、さまざまな#のパラメータで動作します。これを行うために書き直すのはそれほど難しいことではないかもしれませんが、ソースコードはまだリリースされていないため、醜い逆アセンブルを見ているだけで立ち往生しています
Andrew Backer

2
+1が機能しない理由、およびWeb APIの背後にある哲学を実際に説明するため。
MEMark

内訳を理解してください...コントローラごとに1つのPOST / PUT / GETであると想定していましたが、確信が持てませんでした...したがって、なぜそれを調べたのかを説明します。コントローラーごとに複数の同様のアクションが標準であるWebアプリ用のMVCを使用して開発を始めたので、それはほとんど無駄に思えるので、開発者がなぜそうしたいのか理解できます。コントローラーが多すぎるなどはありませんか?
Anthony Griggs

6

同じWeb APIコントローラーに複数のGetメソッドとPostメソッドを追加することが可能です。ここで、デフォルトルートは問題の原因です。Web APIは、ルートが上から下に一致するかどうかをチェックするため、デフォルトのルートはすべてのリクエストに一致します。デフォルトルートでは、1つのコントローラーで1つのGetおよびPostメソッドのみが可能です。次のコードを上に配置するか、デフォルトルートをコメント化/削除します

    config.Routes.MapHttpRoute("API Default", 
                               "api/{controller}/{action}/{id}",
                               new { id = RouteParameter.Optional });

1

ルートプレフィックス[RoutePrefix( "api / Profiles")]をコントローラーレベルに配置し、アクションメソッド[Route( "LikeProfile")]にルートを配置します。global.asaxファイルで何も変更する必要はありません

namespace KhandalVipra.Controllers
{
    [RoutePrefix("api/Profiles")]
    public class ProfilesController : ApiController
    {
        // POST: api/Profiles/LikeProfile
        [Authorize]
        [HttpPost]
        [Route("LikeProfile")]
        [ResponseType(typeof(List<Like>))]
        public async Task<IHttpActionResult> LikeProfile()
        {
        }
    }
}

0

質問はすでに答えられていると思います。また、署名されたメソッドが同じで名前が異なるwebApiコントローラーを探していました。電卓をWebApiとして実装しようとしていました。電卓には、同じシグネチャで名前が異なる4つのメソッドがあります。

public class CalculatorController : ApiController
{
    [HttpGet]
    [ActionName("Add")]
    public string Add(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Add = {0}", num1 + num2);
    }

    [HttpGet]
    [ActionName("Sub")]
    public string Sub(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Subtract result = {0}", num1 - num2);
    }

    [HttpGet]
    [ActionName("Mul")]
    public string Mul(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Multiplication result = {0}", num1 * num2);
    }

    [HttpGet]
    [ActionName("Div")]
    public string Div(int num1 = 1, int num2 = 1, int timeDelay = 1)
    {
        Thread.Sleep(1000 * timeDelay);
        return string.Format("Division result = {0}", num1 / num2);
    }
}

そしてすでに持っているWebApiConfigファイルに

 config.Routes.MapHttpRoute(
            name: "DefaultApi",
            routeTemplate: "api/{controller}/{action}/{id}",
            defaults: new { id = RouteParameter.Optional });

IISで認証/承認を設定するだけで完了です!

お役に立てれば!


0

このアプローチを使用できます:

public class VTRoutingController : ApiController
{
    [HttpPost("Route")]
    public MyResult Route(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }

    [HttpPost("TSPRoute")]
    public MyResult TSPRoute(MyRequestTemplate routingRequestTemplate)
    {
        return null;
    }
}

-1
public class Journal : ApiController
{
    public MyResult Get(journal id)
    {
        return null;
    }
}

public class Journal : ApiController
{

    public MyResult Get(journal id, publication id)
    {
        return null;
    }
}

get / postメソッドのオーバーロードがrestfull apiの概念に違反しているかどうかはわかりませんが、うまくいきます。誰かがこの問題について啓発できたとしたら。私がURIを持っているとどうなりますか

uri:/api/journal/journalid
uri:/api/journal/journalid/publicationid

私のジャーナルの一種のaggregaterootをご覧になったかもしれませんが、パブリケーション用に別のコントローラーを単独で定義し、私のIDでパブリケーションのID番号を渡すことができますが、これははるかに意味があります。私の出版物はジャーナルなしでは存在しなかったからです。


-1

「action = action_name」をURLに追加しただけで、ルーティングエンジンは必要なアクションを認識します。ActionName属性もアクションに追加しましたが、それが必要かどうかはわかりません。


-1

このトピックで私が見た中で最も簡単な説明 -http://www.binaryintellect.net/articles/9db02aa1-c193-421e-94d0-926e440ed297.aspx

  • 編集済み-

Routeのみで機能し、RoutePrefixは必要ありませんでした。

たとえば、コントローラで

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomer
([FromBody]CustomerOrder obj)
{
}

そして

[HttpPost]
[Route("[action]")]
public IActionResult PostCustomerAndOrder
([FromBody]CustomerOrder obj)
{
}

次に、関数名はjqueryにどちらかとして入ります-

options.url = "/api/customer/PostCustomer";

または

options.url = "/api/customer/PostCustomerAndOrder";
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.