ASP.NET Web APIのカスタムメソッド名


110

WCF Web APIから新しいASP.NET MVC 4 Web APIに変換しています。UsersControllerがあり、Authenticateという名前のメソッドが必要です。GetAll、GetOne、Post、およびDeleteの方法の例が表示されますが、これらのサービスにメソッドを追加したい場合はどうなりますか?たとえば、UsersServiceには、ユーザー名とパスワードを渡すAuthenticateというメソッドが必要ですが、機能しません。

public class UsersController : BaseApiController
{
    public string GetAll()
    {
        return "getall!";
    }

    public string Get(int id)
    {
        return "get 1! " + id;
    }

    public User GetAuthenticate(string userName, string password, string applicationName)
    {
        LogWriter.Write(String.Format("Received authenticate request for username {0} and password {1} and application {2}",
            userName, password, applicationName));

        //check if valid leapfrog login.
        var decodedUsername = userName.Replace("%40", "@");
        var encodedPassword = password.Length > 0 ? Utility.HashString(password) : String.Empty;
        var leapFrogUsers = LeapFrogUserData.FindAll(decodedUsername, encodedPassword);

        if (leapFrogUsers.Count > 0)
        {
            return new User
            {
                Id = (uint)leapFrogUsers[0].Id,
                Guid = leapFrogUsers[0].Guid
            };
        }
        else
            throw new HttpResponseException("Invalid login credentials");
    }
}

myapi / api / users /を参照してGetAllを呼び出し、myapi / api / users / 1を参照してGetを呼び出すことができますが、myapi / api / users / authenticate?username = {0}を呼び出すと&password = {1}の場合、Get(認証ではない)とエラーが呼び出されます。

パラメータディクショナリに、 'Navtrak.Services.WCF.NavtrakAPI.Controllers.UsersController'のメソッド 'System.String Get(Int32)'のnullにできないタイプ 'System.Int32'のパラメータ 'id'のnullエントリが含まれています。オプションのパラメーターは、参照型、null許容型、またはオプションのパラメーターとして宣言する必要があります。

Authenticateなどのカスタムメソッド名を呼び出すにはどうすればよいですか?


第五回答:このリンクを参照してくださいstackoverflow.com/questions/12775590/...
Vishwa G

回答:


136

デフォルトでは、ルート構成はRESTFulの規則に従います。つまり、Get、Post、Put、Deleteのアクション名のみを受け入れます(global.asaxでルートを確認してください=>デフォルトでは、アクション名を指定することはできません=> HTTP動詞を使用してディスパッチします)。したがって、GETリクエストを送信する/api/users/authenticateと、基本的にGet(int id)アクションが呼び出されて渡さid=authenticateれます。これは、Getアクションが整数を期待しているために明らかにクラッシュします。

標準のアクション名とは異なるアクション名が必要な場合は、次の場所でルート定義を変更できますglobal.asax

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

これで/api/values/getauthenticate、ユーザーを認証するためにナビゲートできます。


20
Get(id)、Get()、Put、Delete、Postを使用しながら、他のアクションを許可する方法はありますか?
Shawn Mclean 2012年

@ShawnMclean {action}制約なしで別のルートを指定して、または{id}以外(または何でも)が一致しないようにすることができると思います。その後、Darinの提案したものに陥るはずですintGuid
Steve Greatrex

2
ここでもう1つ重要なことは、このスタイルのルーティングでは、許可されたHTTPメソッド([HttpGet]など)を指定するために属性を使用する必要があることです。
Himalaya Garg 2016年

1
他のアクションを使用する必要がありますか?あなたは本当にあなたがしていることをREST規約の範囲内に収めようとしましたか?他のアクションを使用する必要はありません。
niico

1
@niico:Get()が返す要素の数を返すCount()メソッドが必要だと想像してください。これをGet()、Get(id)、Post(...)、Put(...)、またはDelete(id)に適合させる方法がわかりません。そしてもちろん、想像できるより多くの可能な方法があります。
Jens Mander

88

これは、通常のRESTメソッドもサポートしながら、追加のGETメソッドを組み込むためにこれまでに思いついた最良の方法です。以下のルートをWebApiConfigに追加します:

routes.MapHttpRoute("DefaultApiWithId", "Api/{controller}/{id}", new { id = RouteParameter.Optional }, new { id = @"\d+" });
routes.MapHttpRoute("DefaultApiWithAction", "Api/{controller}/{action}");
routes.MapHttpRoute("DefaultApiGet", "Api/{controller}", new { action = "Get" }, new { httpMethod = new HttpMethodConstraint(HttpMethod.Get) });
routes.MapHttpRoute("DefaultApiPost", "Api/{controller}", new {action = "Post"}, new {httpMethod = new HttpMethodConstraint(HttpMethod.Post)});

以下のテストクラスでこのソリューションを検証しました。以下のコントローラーで各メソッドを正常にヒットできました:

public class TestController : ApiController
{
    public string Get()
    {
        return string.Empty;
    }

    public string Get(int id)
    {
        return string.Empty;
    }

    public string GetAll()
    {
        return string.Empty;
    }

    public void Post([FromBody]string value)
    {
    }

    public void Put(int id, [FromBody]string value)
    {
    }

    public void Delete(int id)
    {
    }
}

次のリクエストをサポートしていることを確認しました。

GET /Test
GET /Test/1
GET /Test/GetAll
POST /Test
PUT /Test/1
DELETE /Test/1

あなたの余分なGETアクションがで始まらない場合は、メソッドにHTTPGET属性を追加することもGET「」は。


1
素敵な解決策は、私が設定している場合、あなたは私に言うことができるputし、deleteあなたが上で行ったように、動詞などをgetしてpost、あまりにも正常に動作しますか?
フェリペオリアーニ2013年

1
私の意見では、これはWebAPIプロジェクトのデフォルトに含める必要があります(コメント化されている可能性があります)。WebAPIとMVCスタイルのルートを同時に提供します...
John Culviner 2013年

1
@FelipeOriani、これらの要求は通常、その操作を適用するリソースを識別するためのidパラメーターを伴うため、構成putまたはdelete動詞を必要とする、または必要とは思わないでしょう。deleteするために呼び出し/api/fooたfooの削除しようとしているため、エラーをスローする必要がありますか?したがって、DefaultApiWithIdルートはこれらのケースを適切に処理する必要があります。
nwayve 2013年

4
これは私にはまったくうまくいきませんでした。基本的なGETを実行しようとすると、エラーメッセージが表示されました。
マット

最初のDefaultApiWithIdの場合、デフォルトはnew {id = RouteParameter.Optional}ではなくnullにすべきではありませんか?「ID」は必要ありませんか?
ジョニーオシカ2014年

22

私はMVC4の世界に日々入っています。

その価値については、SitesAPIControllerがあり、次のように呼び出すことができるカスタムメソッドが必要です。

http://localhost:9000/api/SitesAPI/Disposition/0

最後のパラメーターに異なる値を使用して、異なる性質を持つレコードを取得します。

最後に私のために働いたのは:

SitesAPIControllerのメソッド:

// GET api/SitesAPI/Disposition/1
[ActionName("Disposition")]
[HttpGet]
public Site Disposition(int disposition)
{
    Site site = db.Sites.Where(s => s.Disposition == disposition).First();
    return site;
}

そして、これはWebApiConfig.cs

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

// this i added
config.Routes.MapHttpRoute(
    name: "Action",
    routeTemplate: "api/{controller}/{action}/{disposition}"
 );

私が遭遇していた{disposition}を{id}と命名している限り:

{
"Message": "No HTTP resource was found that matches the request URI 'http://localhost:9000/api/SitesAPI/Disposition/0'.",
"MessageDetail": "No action was found on the controller 'SitesAPI' that matches the request."
}

名前を{disposition}に変更すると、機能し始めました。したがって、明らかにパラメーター名はプレースホルダーの値と一致します。

より正確でわかりやすいように、この回答を自由に編集してください。


先端をありがとう。私はあなたと同じ過ちを犯していた。
abhi 14

16

Web APIはデフォルトで、このデフォルトのルーティングをオーバーライドするために、api / {controller} / {id}の形式のURLを想定しています。以下の2つの方法のいずれかでルーティングを設定できます。

最初のオプション:

以下のルート登録をWebApiConfig.csに追加します

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

以下のように、HttpGetとパラメータでアクションメソッドを装飾します。

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

 {

// your code here

}

上記のメソッドのURLを呼び出すためのURLは以下のようになります

http:// localhost:[yourport] / api / MyData / ReadMyData?param1 = value1&param2 = value2&param3 = value3

2番目のオプション コントローラークラスにルートプレフィックスを追加し、以下のようにHttpGetでアクションメソッドを装飾します。この場合、WebApiConfig.csを変更する必要はありません。デフォルトのルーティングを持つことができます。

[RoutePrefix("api/{controller}/{action}")]
public class MyDataController : ApiController
{

[HttpGet]
public HttpResponseMessage ReadMyData(string param1,
                        string param2, string param3)

{

// your code here

}

}

上記のメソッドのURLを呼び出すためのURLは以下のようになります

http:// localhost:[yourport] / api / MyData / ReadMyData?param1 = value1&param2 = value2&param3 = value3


私は2番目のオプションがとても好きです。VB.netでの使用方法も教えてください。どうもありがとう。
user1617676 2017年

12

ASP.NET 5ASP.NET MVC 6を使用している場合、通常、MVCに(デフォルトのRESTful規則を使用して)適切なルートコレクションを作成させるため、これらの回答のほとんどは機能しません。思いのままRoutes.MapRoute()に編集する呼び出しはありません。

ConfigureServices()呼び出されたメソッドStartup.csを呼び出すとき、その方法:ファイルには、ASP.NET 5に組み込まれた依存性注入フレームワークでMVCを登録しますApplicationBuilder.UseMvc()、後でそのクラスで、MVCフレームワークは自動的にあなたのアプリケーションにこれらのデフォルトルートを追加します。UseMvc()フレームワークのソースコード内のメソッドの実装を確認することで、内部で何が起こっているかを確認できます。

public static IApplicationBuilder UseMvc(
    [NotNull] this IApplicationBuilder app,
    [NotNull] Action<IRouteBuilder> configureRoutes)
{
    // Verify if AddMvc was done before calling UseMvc
    // We use the MvcMarkerService to make sure if all the services were added.
    MvcServicesHelper.ThrowIfMvcNotRegistered(app.ApplicationServices);

    var routes = new RouteBuilder
    {
        DefaultHandler = new MvcRouteHandler(),
        ServiceProvider = app.ApplicationServices
    };

    configureRoutes(routes);

    // Adding the attribute route comes after running the user-code because
    // we want to respect any changes to the DefaultHandler.
    routes.Routes.Insert(0, AttributeRouting.CreateAttributeMegaRoute(
        routes.DefaultHandler,
        app.ApplicationServices));

    return app.UseRouter(routes.Build());
}

これの良い点は、フレームワークがすべての困難な作業を処理し、すべてのコントローラーのアクションを反復処理し、それらのデフォルトルートを設定することで、冗長な作業を節約できることです。

悪い点は、独自のルートを追加する方法に関するドキュメントがほとんどまたはまったくないことです。幸いなことに、コンベンションベースおよび/または属性ベースのアプローチ(別名アトリビュートルーティング)を使用することで、簡単にそれを行うことができます。

コンベンションベース

Startup.csクラスで、これを置き換えます。

app.UseMvc();

これとともに:

app.UseMvc(routes =>
            {
                // Route Sample A
                routes.MapRoute(
                    name: "RouteSampleA",
                    template: "MyOwnGet",
                    defaults: new { controller = "Items", action = "Get" }
                );
                // Route Sample B
                routes.MapRoute(
                    name: "RouteSampleB",
                    template: "MyOwnPost",
                    defaults: new { controller = "Items", action = "Post" }
                );
            });

属性ベース

MVC6の素晴らしいところは、あなたがまた、いずれかの装飾によりごとのコントローラごとにルートを定義することができるということですControllerクラスおよび/またはAction適切と方法RouteAttributeおよび/またはHttpGet/ HttpPost次のようなテンプレートパラメータを、:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNet.Mvc;

namespace MyNamespace.Controllers
{
    [Route("api/[controller]")]
    public class ItemsController : Controller
    {
        // GET: api/items
        [HttpGet()]
        public IEnumerable<string> Get()
        {
            return GetLatestItems();
        }

        // GET: api/items/5
        [HttpGet("{num}")]
        public IEnumerable<string> Get(int num)
        {
            return GetLatestItems(5);
        }       

        // GET: api/items/GetLatestItems
        [HttpGet("GetLatestItems")]
        public IEnumerable<string> GetLatestItems()
        {
            return GetLatestItems(5);
        }

        // GET api/items/GetLatestItems/5
        [HttpGet("GetLatestItems/{num}")]
        public IEnumerable<string> GetLatestItems(int num)
        {
            return new string[] { "test", "test2" };
        }

        // POST: /api/items/PostSomething
        [HttpPost("PostSomething")]
        public IActionResult Post([FromBody]string someData)
        {
            return Content("OK, got it!");
        }
    }
}

このコントローラーは、次の要求を処理します。

 [GET] api/items
 [GET] api/items/5
 [GET] api/items/GetLatestItems
 [GET] api/items/GetLatestItems/5
 [POST] api/items/PostSomething

また、2つのアプローチを使用すると、属性ベースのルート(定義されている場合)がコンベンションベースのルートをオーバーライドし、両方がによって定義されたデフォルトルートをオーバーライドすることにも注意してくださいUseMvc()

詳細については、私のブログの次の投稿読むこともできます。


1
これは完璧です!他の答えはどれも実際に必要なことをしていませんでした。しかし、あなたは私を救った:)
アーサー王3

事前定義されたモデルを2番目のパラメーターとして使用する方法はありますか?たとえば、次のように特定のユーザーにパッチを適用している場合 public IActionResult Patch(int id, [FromQuery] Person person)すべての受信プロパティはnullです。
アーサー王3


0

以下のようにWebAPIConfig.csを変更するだけです

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

次に、次のようにAPIを実装します

    // GET: api/Controller_Name/Show/1
    [ActionName("Show")]
    [HttpGet]
    public EventPlanner Id(int id){}

0

Web APi 2以降のバージョンでは、属性ルーティングと呼ばれる新しいタイプのルーティングがサポートされています。名前が示すように、属性ルーティングは属性を使用してルートを定義します。属性ルーティングにより、Web APIのURIをより詳細に制御できます。たとえば、リソースの階層を記述するURIを簡単に作成できます。

例えば:

[Route("customers/{customerId}/orders")]
public IEnumerable<Order> GetOrdersByCustomer(int customerId) { ... }

完璧になり、WebApiConfig.csなどの追加のコードは必要ありません。以下のようにアクティブ化できない場合は、WebApiConfig.csでWeb APIルーティングが有効になっているかどうかを確認する必要があります。

        // Web API routes
        config.MapHttpAttributeRoutes();

WebApiConfig.csで何かをしたり、何かを変更したりする必要はありません。詳細については、この記事をご覧ください。

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