web-dev-qa-db-ja.com

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

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許容型、またはオプションのパラメーターとして宣言する必要があります。

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

106
Justin

デフォルトでは、ルート構成はRESTFulの規則に従います。つまり、Get、Post、Put、Deleteアクション名のみを受け入れます(global.asaxのルートを見る=>デフォルトでは、アクション名を指定することはできません=> HTTP動詞を使用してディスパッチします)。したがって、/api/users/authenticateにGETリクエストを送信すると、基本的に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に移動してユーザーを認証できます。

126
Darin Dimitrov

これは、通常の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アクションが「Get」で始まらない場合は、メソッドにHttpGet属性を追加できます。

85
sky-dev

私は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}に変更すると、機能し始めました。そのため、パラメータ名はプレースホルダーの値と一致しているようです。

この回答を自由に編集して、より正確/説明的にしてください。

18
Kinjal Dixit

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は以下のようになります

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

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は以下のようになります

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

14

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

Startup.csファイルによって呼び出されたConfigureServices()メソッドは、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());
}

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

悪いことは、独自のルートを追加する方法についてのドキュメントがほとんどまたはまったくないことです。幸いなことに、Convention-Basedおよび/またはAttribute-Basedアプローチ(別名Attribute Routing)。

コンベンションベース

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()で定義されたデフォルトルートをオーバーライドします。

詳細については、ブログで 次の投稿を読む をご覧ください。

10
Darkseal

名前付きアクションの詳細については、この記事を参照してください。また、アクション名の前に「get」を付ける代わりに[HttpGet]属性を使用できることも示しています。

http://www.asp.net/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api

9
Andrew