ASP.NET Web API 2 で属性のルーティングAttribute Routing in ASP.NET Web API 2

作成者Mike Wassonby Mike Wasson

ルーティングWeb API がアクションへの URI に一致します。Routing is how Web API matches a URI to an action. 新しい型をサポートする web API 2 のルーティングと呼ばれる属性ルーティングします。Web API 2 supports a new type of routing, called attribute routing. 名前が示すようは、ルートを定義するのに属性を使用する属性ルーティングします。As the name implies, attribute routing uses attributes to define routes. 属性ルーティングでは、Uri の制御、web API でします。Attribute routing gives you more control over the URIs in your web API. たとえば、リソースの階層について説明する Uri を簡単に作成することができます。For example, you can easily create URIs that describe hierarchies of resources.

規約ベースと呼ばれる、ルーティングの以前のスタイルを引き続き完全にサポートは、ルーティングします。The earlier style of routing, called convention-based routing, is still fully supported. 実際には、同じプロジェクト内の両方の手法を組み合わせることができます。In fact, you can combine both techniques in the same project.

このトピックでは、属性ルーティングを有効にする方法を示していて、属性ルーティングのさまざまなオプションについて説明します。This topic shows how to enable attribute routing and describes the various options for attribute routing. 属性ルーティングを使用するエンド ツー エンド チュートリアルでは、次を参照してください。属性ルーティングで Web API 2 で REST API の作成です。For an end-to-end tutorial that uses attribute routing, see Create a REST API with Attribute Routing in Web API 2.

必須コンポーネントPrerequisites

Visual Studio 2017 Community、Professional、または Enterprise editionVisual Studio 2017 Community, Professional, or Enterprise edition

または、NuGet パッケージ マネージャーを使用して、必要なパッケージをインストールします。Alternatively, use NuGet Package Manager to install the necessary packages. ツール メニューの選択 Visual Studio でNuGet パッケージ マネージャーを選択し、パッケージ マネージャー コンソールします。From the Tools menu in Visual Studio, select NuGet Package Manager, then select Package Manager Console. パッケージ マネージャー コンソール ウィンドウで、次のコマンドを入力します。Enter the following command in the Package Manager Console window:

Install-Package Microsoft.AspNet.WebApi.WebHost

なぜ属性ルーティングでしょうか。Why Attribute Routing?

使用する Web API の最初のリリース規則に基づくルーティングします。The first release of Web API used convention-based routing. その型のルーティングでは、いずれかを定義するかは基本的に、複数のルート テンプレートには、文字列がパラメーター化されました。In that type of routing, you define one or more route templates, which are basically parameterized strings. フレームワークは、要求を受信したときに、ルート テンプレートに対して URI が一致します。When the framework receives a request, it matches the URI against the route template. (規約ベースのルーティングの詳細については、次を参照してください。 ASP.NET Web API におけるルーティングします。(For more information about convention-based routing, see Routing in ASP.NET Web API.

規約ベースのルーティングの利点の 1 つは、テンプレートが 1 つの場所で定義されていること、およびルーティング規則は、すべてのコント ローラー間で一貫して適用されます。One advantage of convention-based routing is that templates are defined in a single place, and the routing rules are applied consistently across all controllers. 残念ながら、規約ベースのルーティングを困難に RESTful Api に共通している特定の URI パターンをサポートします。Unfortunately, convention-based routing makes it hard to support certain URI patterns that are common in RESTful APIs. たとえば、リソースには多くの場合、子リソースが含まれます。顧客の注文がある、映画のアクターがあると、ブックの「複数の作者によりやなど。For example, resources often contain child resources: Customers have orders, movies have actors, books have authors, and so forth. これらの関係を反映する Uri を作成する自然です。It's natural to create URIs that reflect these relations:

/customers/1/orders

この種類の URI では、規則ベースのルーティングを使用して作成を困難です。This type of URI is difficult to create using convention-based routing. これを行うことができます、結果は、多くのコント ローラーやリソースの種類がある場合にもスケールはありません。Although it can be done, the results don't scale well if you have many controllers or resource types.

属性ルーティングは、この URI のルートを定義も簡単です。With attribute routing, it's trivial to define a route for this URI. コント ローラー アクションに属性を追加するだけです。You simply add an attribute to the controller action:

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

属性ルーティングで簡単に他のいくつかのパターンを次に示します。Here are some other patterns that attribute routing makes easy.

API のバージョン管理API versioning

この例では、「/api/v1/製品」より別のコント ローラーにルーティング「/api/v2/製品」になります。In this example, "/api/v1/products" would be routed to a different controller than "/api/v2/products".

/api/v1/products /api/v2/products

オーバー ロードされた URI セグメントOverloaded URI segments

この例では、「1」は、注文番号がコレクションに「保留中」にマップされます。In this example, "1" is an order number, but "pending" maps to a collection.

/orders/1 /orders/pending

複数のパラメーター型Multiple parameter types

この例では、「1」は、注文番号が「2013/06/16」を日付を指定します。In this example, "1" is an order number, but "2013/06/16" specifies a date.

/orders/1 /orders/2013/06/16

属性ルーティングを有効にします。Enabling Attribute Routing

属性ルーティングを有効にするのには、呼び出すMapHttpAttributeRoutes構成中にします。To enable attribute routing, call MapHttpAttributeRoutes during configuration. この拡張メソッドが定義されている、 System.Web.Http.HttpConfigurationExtensionsクラス。This extension method is defined in the System.Web.Http.HttpConfigurationExtensions class.

using System.Web.Http;

namespace WebApplication
{
    public static class WebApiConfig
    {
        public static void Register(HttpConfiguration config)
        {
            // Web API routes
            config.MapHttpAttributeRoutes();

            // Other Web API configuration not shown.
        }
    }
}

属性のルーティングを組み合わせる規則に基づくルーティングします。Attribute routing can be combined with convention-based routing. 規約ベースのルーティングを定義するには、呼び出し、 MapHttpRouteメソッド。To define convention-based routes, call the MapHttpRoute method.

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Attribute routing.
        config.MapHttpAttributeRoutes();

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

Web API を構成する方法の詳細については、次を参照してください。 ASP.NET Web API 2 の構成します。For more information about configuring Web API, see Configuring ASP.NET Web API 2.

メモ:Web API 1 からの移行Note: Migrating From Web API 1

Web API 2 では、前に、Web API プロジェクト テンプレートには、このようなコードが生成されます。Prior to Web API 2, the Web API project templates generated code like this:

protected void Application_Start()
{
    // WARNING - Not compatible with attribute routing.
    WebApiConfig.Register(GlobalConfiguration.Configuration);
}

属性ルーティングを有効にすると、このコードで例外がスローされます。If attribute routing is enabled, this code will throw an exception. 属性ルーティングを使用する既存の Web API プロジェクトをアップグレードする場合は、次にこの構成コードを更新することを確認してください。If you upgrade an existing Web API project to use attribute routing, make sure to update this configuration code to the following:

protected void Application_Start()
{
    // Pass a delegate to the Configure method.
    GlobalConfiguration.Configure(WebApiConfig.Register);
}

Note

詳細については、次を参照してください。 ASP.NET ホストによる Web API を構成するします。For more information, see Configuring Web API with ASP.NET Hosting.

ルート属性を追加します。Adding Route Attributes

属性を使用して定義されているルートの例を次に示します。Here is an example of a route defined using an attribute:

public class OrdersController : ApiController
{
    [Route("customers/{customerId}/orders")]
    [HttpGet]
    public IEnumerable<Order> FindOrdersByCustomer(int customerId) { ... }
}

文字列"顧客/{customerId}/orders"はルートの URI テンプレートです。The string "customers/{customerId}/orders" is the URI template for the route. Web API は、テンプレートには、要求 URI の一致を試みます。Web API tries to match the request URI to the template. この例では、"customers"と"orders"は、リテラルのセグメントと"{customerId}"が変数のパラメーター。In this example, "customers" and "orders" are literal segments, and "{customerId}" is a variable parameter. 次の Uri には、このテンプレートは一致します。The following URIs would match this template:

  • http://localhost/customers/1/orders
  • http://localhost/customers/bob/orders
  • http://localhost/customers/1234-5678/orders

使用して照合を制限する制約、このトピックの後半で説明します。You can restrict the matching by using constraints, described later in this topic.

注意、 "{customerId}"ルート テンプレートのパラメーターの名前に一致する、 customerIdメソッドのパラメーター。Notice that the "{customerId}" parameter in the route template matches the name of the customerId parameter in the method. Web API コント ローラー アクションを呼び出すと、ルート パラメーターをバインドしようとします。When Web API invokes the controller action, it tries to bind the route parameters. たとえば、URI は、 http://example.com/customers/1/orders、Web API が「1」の値をバインドするを試みます、 customerIdアクションのパラメーター。For example, if the URI is http://example.com/customers/1/orders, Web API tries to bind the value "1" to the customerId parameter in the action.

URI テンプレートでは、いくつかのパラメーターを持つことができます。A URI template can have several parameters:

[Route("customers/{customerId}/orders/{orderId}")]
public Order GetOrderByCustomer(int customerId, int orderId) { ... }

ルート属性を持たないコント ローラーのメソッドは、規則ベースのルーティングを使用します。Any controller methods that do not have a route attribute use convention-based routing. これにより、同じプロジェクトでのルーティングの両方の種類を組み合わせることができます。That way, you can combine both types of routing in the same project.

HTTP メソッドHTTP Methods

Web API には、(GET、POST など) の要求の HTTP メソッドに基づいてアクションも選択します。Web API also selects actions based on the HTTP method of the request (GET, POST, etc). 既定では、Web API コント ローラーのメソッド名の先頭の大文字と小文字を探します。By default, Web API looks for a case-insensitive match with the start of the controller method name. という名前のコント ローラー メソッドなど、 PutCustomers HTTP PUT 要求と一致します。For example, a controller method named PutCustomers matches an HTTP PUT request.

次の属性のいずれかのメソッドを修飾することでこの規則をオーバーライドできます。You can override this convention by decorating the method with any the following attributes:

  • [HttpDelete][HttpDelete]
  • [HttpGet][HttpGet]
  • [HttpHead][HttpHead]
  • [HttpOptions][HttpOptions]
  • [HttpPatch][HttpPatch]
  • [HttpPost][HttpPost]
  • [HttpPut][HttpPut]

次の例では、HTTP POST 要求に CreateBook メソッドをマップします。The following example maps the CreateBook method to HTTP POST requests.

[Route("api/books")]
[HttpPost]
public HttpResponseMessage CreateBook(Book book) { ... }

標準以外の方法を含む、他のすべての HTTP メソッドを使用して、 AcceptVerbs属性には、HTTP メソッドのリストを取得します。For all other HTTP methods, including non-standard methods, use the AcceptVerbs attribute, which takes a list of HTTP methods.

// WebDAV method
[Route("api/books")]
[AcceptVerbs("MKCOL")]
public void MakeCollection() { }

ルート プレフィックスRoute Prefixes

多くの場合、同じプレフィックスで始まるすべてのコント ローラーにルーティングされます。Often, the routes in a controller all start with the same prefix. 例えば:For example:

public class BooksController : ApiController
{
    [Route("api/books")]
    public IEnumerable<Book> GetBooks() { ... }

    [Route("api/books/{id:int}")]
    public Book GetBook(int id) { ... }

    [Route("api/books")]
    [HttpPost]
    public HttpResponseMessage CreateBook(Book book) { ... }
}

使用してコント ローラー全体の共通のプレフィックスを設定することができます、 [RoutePrefix] 属性。You can set a common prefix for an entire controller by using the [RoutePrefix] attribute:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET api/books
    [Route("")]
    public IEnumerable<Book> Get() { ... }

    // GET api/books/5
    [Route("{id:int}")]
    public Book Get(int id) { ... }

    // POST api/books
    [Route("")]
    public HttpResponseMessage Post(Book book) { ... }
}

メソッドの属性をティルダ (~) を使用して、ルート プレフィックスをオーバーライドします。Use a tilde (~) on the method attribute to override the route prefix:

[RoutePrefix("api/books")]
public class BooksController : ApiController
{
    // GET /api/authors/1/books
    [Route("~/api/authors/{authorId:int}/books")]
    public IEnumerable<Book> GetByAuthor(int authorId) { ... }

    // ...
}

ルートのプレフィックスは、パラメーターを含めることができます。The route prefix can include parameters:

[RoutePrefix("customers/{customerId}")]
public class OrdersController : ApiController
{
    // GET customers/1/orders
    [Route("orders")]
    public IEnumerable<Order> Get(int customerId) { ... }
}

ルート制約Route Constraints

ルート制約では、ルート テンプレートでパラメーターを照合する方法を制限できます。Route constraints let you restrict how the parameters in the route template are matched. 一般的な構文は"{0} パラメーター: 制約}"します。The general syntax is "{parameter:constraint}". 例えば:For example:

[Route("users/{id:int}")]
public User GetUserById(int id) { ... }

[Route("users/{name}")]
public User GetUserByName(string name) { ... }

ここでは、最初のルートは場合にのみオン、 "id" URI のセグメントは、整数です。Here, the first route will only be selected if the "id" segment of the URI is an integer. それ以外の場合、2 つ目のルートが選択されます。Otherwise, the second route will be chosen.

次の表は、サポートされている制約を一覧表示します。The following table lists the constraints that are supported.

制約Constraint 説明Description Example
アルファalpha 一致の大文字または小文字の英数字 (a ~ z、A ~ Z)Matches uppercase or lowercase Latin alphabet characters (a-z, A-Z) {アルファ: x}{x:alpha}
boolbool ブール値と一致します。Matches a Boolean value. {x:bool}{x:bool}
datetimedatetime 一致するDateTime値。Matches a DateTime value. {x:datetime}{x:datetime}
decimaldecimal 10 進値と一致します。Matches a decimal value. {x:decimal}{x:decimal}
doubledouble 64 ビットの浮動小数点値と一致します。Matches a 64-bit floating-point value. {x:double}{x:double}
floatfloat 32 ビットの浮動小数点値と一致します。Matches a 32-bit floating-point value. {x:float}{x:float}
guidguid GUID 値と一致します。Matches a GUID value. {x:guid}{x:guid}
intint 32 ビット整数値と一致します。Matches a 32-bit integer value. {x:int}{x:int}
長さlength 指定した長さで、または長さの指定した範囲内の文字列と一致します。Matches a string with the specified length or within a specified range of lengths. {x:length(6)} {x:length(1,20)}{x:length(6)} {x:length(1,20)}
longlong 64 ビット整数値と一致します。Matches a 64-bit integer value. {x:long}{x:long}
maxmax 最大値は整数に一致します。Matches an integer with a maximum value. {x:max(10)}{x:max(10)}
maxlengthmaxlength 最大長を持つ文字列と一致します。Matches a string with a maximum length. {x:maxlength(10)}{x:maxlength(10)}
min 整数値を最小値と一致します。Matches an integer with a minimum value. {x:min(10)}{x:min(10)}
minlengthminlength 最小長の文字列と一致します。Matches a string with a minimum length. {x:minlength(10)}{x:minlength(10)}
rangerange 値の範囲内の整数に一致します。Matches an integer within a range of values. {x:range(10,50)}{x:range(10,50)}
regexregex 正規表現と一致します。Matches a regular expression. {x:regex(^\d{3}-\d{3}-\d{4}$)}{x:regex(^\d{3}-\d{3}-\d{4}$)}

通知、制約の一部など"min"かっこで囲まれた引数を受け取ります。Notice that some of the constraints, such as "min", take arguments in parentheses. コロンで区切られた、パラメーターには、複数の制約を適用できます。You can apply multiple constraints to a parameter, separated by a colon.

[Route("users/{id:int:min(1)}")]
public User GetUserById(int id) { ... }

カスタム ルート制約Custom Route Constraints

実装することによってカスタム ルート制約を作成することができます、 IHttpRouteConstraintインターフェイス。You can create custom route constraints by implementing the IHttpRouteConstraint interface. たとえば、次の制約は、0 以外の整数値にパラメーターを制限します。For example, the following constraint restricts a parameter to a non-zero integer value.

public class NonZeroConstraint : IHttpRouteConstraint
{
    public bool Match(HttpRequestMessage request, IHttpRoute route, string parameterName, 
        IDictionary<string, object> values, HttpRouteDirection routeDirection)
    {
        object value;
        if (values.TryGetValue(parameterName, out value) && value != null)
        {
            long longValue;
            if (value is long)
            {
                longValue = (long)value;
                return longValue != 0;
            }

            string valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
            if (Int64.TryParse(valueString, NumberStyles.Integer, 
                CultureInfo.InvariantCulture, out longValue))
            {
                return longValue != 0;
            }
        }
        return false;
    }
}

次のコードでは、制約を登録する方法を示します。The following code shows how to register the constraint:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        var constraintResolver = new DefaultInlineConstraintResolver();
        constraintResolver.ConstraintMap.Add("nonzero", typeof(NonZeroConstraint));

        config.MapHttpAttributeRoutes(constraintResolver);
    }
}

これで、ルートで制約を適用できます。Now you can apply the constraint in your routes:

[Route("{id:nonzero}")]
public HttpResponseMessage GetNonZero(int id) { ... }

全体を置換することもできます。 DefaultInlineConstraintResolverクラスを実装することによって、 IInlineConstraintResolverインターフェイス。You can also replace the entire DefaultInlineConstraintResolver class by implementing the IInlineConstraintResolver interface. そうはすべての組み込みの制約の場合を除いて置き換えるの実装IInlineConstraintResolver具体的にはそれらを追加します。Doing so will replace all of the built-in constraints, unless your implementation of IInlineConstraintResolver specifically adds them.

省略可能な URI パラメーターと既定値Optional URI Parameters and Default Values

ルート パラメーターに疑問符を追加することで行うと、URI パラメーターが省略可能です。You can make a URI parameter optional by adding a question mark to the route parameter. ルート パラメーターが省略可能な場合は、メソッド パラメーターの既定値を定義する必要があります。If a route parameter is optional, you must define a default value for the method parameter.

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int?}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid = 1033) { ... }
}

この例で/api/books/locale/1033/api/books/locale同じリソースを返します。In this example, /api/books/locale/1033 and /api/books/locale return the same resource.

または、次のように、ルート テンプレート内の既定値を指定できます。Alternatively, you can specify a default value inside the route template, as follows:

public class BooksController : ApiController
{
    [Route("api/books/locale/{lcid:int=1033}")]
    public IEnumerable<Book> GetBooksByLocale(int lcid) { ... }
}

これは、前の例とほぼ同じですが、既定値が適用される動作のわずかな違いがあります。This is almost the same as the previous example, but there is a slight difference of behavior when the default value is applied.

  • 最初の例 ("{lcid:int?}") で、パラメーターがこの正確な値を反映するため、メソッド パラメーターに直接 1033 の既定値が割り当てられます。In the first example ("{lcid:int?}"), the default value of 1033 is assigned directly to the method parameter, so the parameter will have this exact value.
  • 2 番目の例 ("{lcid:int 1033 の =}")、「1033」の既定値は、モデル バインディング プロセスをたどります。In the second example ("{lcid:int=1033}"), the default value of "1033" goes through the model-binding process. 既定のモデル バインダーは、数値の値 1033 に「1033」に変換されます。The default model-binder will convert "1033" to the numeric value 1033. ただし、別のものを実行するカスタム モデル バインダーを接続する可能性があります。However, you could plug in a custom model binder, which might do something different.

(ほとんどの場合、パイプラインでカスタム モデル バインダーがない限り、2 つの形式と同じになります。)(In most cases, unless you have custom model binders in your pipeline, the two forms will be equivalent.)

ルート名Route Names

Web api では、すべてのルート名を持ちます。In Web API, every route has a name. ルート名は、HTTP 応答でのリンクを含めることができるように、リンクを生成するのに役立ちます。Route names are useful for generating links, so that you can include a link in an HTTP response.

ルート名を指定するには、設定、名前属性のプロパティ。To specify the route name, set the Name property on the attribute. 次の例では、リンクを生成するときに、ルート名を使用する方法と、ルート名を設定する方法を示します。The following example shows how to set the route name, and also how to use the route name when generating a link.

public class BooksController : ApiController
{
    [Route("api/books/{id}", Name="GetBookById")]
    public BookDto GetBook(int id) 
    {
        // Implementation not shown...
    }

    [Route("api/books")]
    public HttpResponseMessage Post(Book book)
    {
        // Validate and add book to database (not shown)

        var response = Request.CreateResponse(HttpStatusCode.Created);

        // Generate a link to the new book and set the Location header in the response.
        string uri = Url.Link("GetBookById", new { id = book.BookId });
        response.Headers.Location = new Uri(uri);
        return response;
    }
}

ルートの順序Route Order

フレームワークでは、ルートの URI と照合しようとして、特定の順序でルートを評価します。When the framework tries to match a URI with a route, it evaluates the routes in a particular order. 順序を指定するには、設定、順序ルート属性のプロパティ。To specify the order, set the Order property on the route attribute. 下位の値は、最初に評価されます。Lower values are evaluated first. 既定の順序の値には 0 です。The default order value is zero.

合計の順序を決定する方法を次に示します。Here is how the total ordering is determined:

  1. 比較、順序ルート属性のプロパティ。Compare the Order property of the route attribute.

  2. ルート テンプレートでは、各 URI セグメントを確認します。Look at each URI segment in the route template. セグメントごとに次のように順序します。For each segment, order as follows:

    1. リテラルのセグメント。Literal segments.
    2. ルート制約を持つパラメーター。Route parameters with constraints.
    3. ルート制約なしのパラメーター。Route parameters without constraints.
    4. 制約のワイルドカード パラメーター セグメント。Wildcard parameter segments with constraints.
    5. 制約なしのワイルドカード パラメーター セグメント。Wildcard parameter segments without constraints.
  3. 同数の場合の場合は、ルートは大文字の序数の文字列の比較によって並べ替えられます (OrdinalIgnoreCase) のルート テンプレート。In the case of a tie, routes are ordered by a case-insensitive ordinal string comparison (OrdinalIgnoreCase) of the route template.

次に例を示します。Here is an example. 次のコント ローラーを定義するとします。Suppose you define the following controller:

[RoutePrefix("orders")]
public class OrdersController : ApiController
{
    [Route("{id:int}")] // constrained parameter
    public HttpResponseMessage Get(int id) { ... }

    [Route("details")]  // literal
    public HttpResponseMessage GetDetails() { ... }

    [Route("pending", RouteOrder = 1)]
    public HttpResponseMessage GetPending() { ... }

    [Route("{customerName}")]  // unconstrained parameter
    public HttpResponseMessage GetByCustomer(string customerName) { ... }

    [Route("{*date:datetime}")]  // wildcard
    public HttpResponseMessage Get(DateTime date) { ... }
}

これらのルートの順序が次のとおりです。These routes are ordered as follows.

  1. orders/詳細orders/details
  2. orders/{id}orders/{id}
  3. orders/{customerName}orders/{customerName}
  4. orders/{*date}orders/{*date}
  5. orders/保留中orders/pending

「詳細」リテラルのセグメントは、および"{id}"、前に表示されますが「保留中」が表示されます最後ため、順序プロパティが 1 にします。Notice that "details" is a literal segment and appears before "{id}", but "pending" appears last because the Order property is 1. (この例ではお客様が指定されていない"details"が「保留中」またはします。(This example assumes there are no customers named "details" or "pending". 一般に、あいまいなルートを回避するためにお試しください。In general, try to avoid ambiguous routes. この例では、より優れたルート テンプレートでGetByCustomerは、"顧客/{customerName}")In this example, a better route template for GetByCustomer is "customers/{customerName}" )