web-dev-qa-db-ja.com

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

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の処理を担当するテンプレートクラスを表します。

エラー:

http://localhost:52370/api/VTRouting/TSPRouteまたはhttp://localhost:52370/api/VTRouting/Routeに対してFiddlerを使用してリクエストを行うと、エラーが発生します。

要求に一致する複数のアクションが見つかりました

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

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のjsonをRequestBodyに渡します。

122
Habib

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}"
    );
    
139
Asif Mushtaq

あなたの問題に対するより良い解決策は、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;
    }
}
32
Wisienkas

つかいます:

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のすべての素晴らしい機能(ハンドラーの委任、コンテンツネゴシエーション、mediatypeformatterなど)を引き続き活用できます。RESTfulアプローチを捨てるだけです。

26
Filip W

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

必然的に、/api/VTRoutingには、送信するパラメーターを受け入れるoneHttpPostメソッドしか存在できません。 [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) {}
}

Ajaxを使用するために多くのことを行う1つのクラスを作成する場合、標準のコントローラー/アクションパターンを使用しない大きな理由はありません。唯一の本当の違いは、メソッドシグネチャがそれほどきれいではないことであり、それらを返す前に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' } )
11
Andrew Backer

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

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

ルートプレフィックス[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()
        {
        }
    }
}
1
Sushil Kumar

質問はすでに答えられていると思います。また、署名された同じ方法で名前が異なる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
Yawar Murtaza

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

0
Rony Tesler