次の方法で共有


ASP.NET Core でアプリケーション モデルを操作する

作成者: Steve Smith

ASP.NET Core MVC は、MVC アプリのコンポーネントを表す アプリケーション モデル を定義します。 MVC 要素の動作を変更するには、このモデルを読み取って操作します。 既定では、MVC は特定の規則に従って、コントローラーと見なされるクラス、アクションであるクラスのメソッド、およびパラメーターとルーティングの動作を決定します。 カスタム規則を作成し、グローバルまたは属性として適用することで、アプリのニーズに合わせてこの動作をカスタマイズします。

モデルとプロバイダー (IApplicationModelProvider)

ASP.NET Core MVC アプリケーション モデルには、MVC アプリケーションを記述する抽象インターフェイスと具象実装クラスの両方が含まれています。 このモデルは、MVC が既定の規則に従ってアプリのコントローラー、アクション、アクション パラメーター、ルート、フィルターを検出した結果です。 アプリケーション モデルを使用して、既定の MVC 動作とは異なる規則に従うようにアプリを変更します。 パラメーター、名前、ルート、フィルターはすべて、アクションとコントローラーの構成データとして使用されます。

ASP.NET Core MVC アプリケーション モデルには、次の構造があります。

  • ApplicationModel
    • コントローラー (ControllerModel)
      • アクション (ActionModel)
        • パラメータ (ParameterModel)

モデルの各レベルは共通の Properties コレクションにアクセスでき、下位レベルでは階層内の上位レベルで設定されたプロパティ値にアクセスして上書きできます。 プロパティは、アクションの作成時に ActionDescriptor.Properties に保持されます。 その後、要求が処理されるときに、追加または変更された規則のすべてのプロパティに、 ActionContext.ActionDescriptor経由でアクセスできます。 プロパティの使用は、フィルター、モデル バインダー、およびその他のアプリ モデルの側面をアクションごとに構成する優れた方法です。

ActionDescriptor.Properties コレクションは、アプリの起動後にスレッド セーフではありません (書き込み用)。 規則は、このコレクションにデータを安全に追加する最善の方法です。

ASP.NET Core MVC は、 IApplicationModelProvider インターフェイスによって定義されたプロバイダー パターンを使用してアプリケーション モデルを読み込みます。 このセクションでは、このプロバイダーの機能に関する内部実装の詳細について説明します。 プロバイダー パターンの使用は、フレームワークでの利用を主とした高度な技術です。 ほとんどのアプリでは、プロバイダー パターンではなく規則を使用する必要があります。

IApplicationModelProvider インターフェイスの実装は互いを "ラップ" します。各実装は、OnProvidersExecuting プロパティに基づいて昇順でOrderを呼び出します。 その後、 OnProvidersExecuted メソッドは逆の順序で呼び出されます。 フレームワークでは、いくつかのプロバイダーを定義します。

最初 (Order=-1000):

  • DefaultApplicationModelProvider

次に (Order=-990):

  • AuthorizationApplicationModelProvider
  • CorsApplicationModelProvider

Orderの同じ値を持つ 2 つのプロバイダーが呼び出される順序は未定義であり、依存しないようにする必要があります。

IApplicationModelProvider は、フレームワーク作成者が拡張するための高度な概念です。 一般に、アプリでは規則を使用し、フレームワークではプロバイダーを使用する必要があります。 主な違いは、プロバイダーは常に規則の前に実行されるということです。

DefaultApplicationModelProviderは、ASP.NET Core MVC で使用される既定の動作の多くを確立します。 その責任は次のとおりです。

  • コンテキストへのグローバル フィルターの追加
  • コンテキストへのコントローラーの追加
  • アクションとしてのパブリック コントローラー メソッドの追加
  • コンテキストへのアクション メソッド パラメーターの追加
  • ルートとその他の属性の適用

一部の組み込み動作は、 DefaultApplicationModelProviderによって実装されます。 このプロバイダーは、 ControllerModelを構築する役割を担います。これにより、 ActionModelPropertyModel、および ParameterModel インスタンスが参照されます。 DefaultApplicationModelProvider クラスは、将来変更される可能性がある内部フレームワーク実装の詳細です。

AuthorizationApplicationModelProviderは、AuthorizeFilterおよびAllowAnonymousFilter属性に関連付けられている動作を適用します。 詳細については、「 ASP.NET Core での単純な承認」を参照してください。

CorsApplicationModelProviderは、IEnableCorsAttributeIDisableCorsAttributeに関連付けられた動作を実装します。 詳細については、「 ASP.NET Core でクロスオリジン要求 (CORS) を有効にする」を参照してください。

このセクションで説明するフレームワークの内部プロバイダーに関する情報は、 .NET API ブラウザーでは使用できません。 ただし、プロバイダーは 、ASP.NET Core 参照ソース (dotnet/aspnetcore GitHub リポジトリ) で検査できます。 GitHub 検索を使用して名前でプロバイダーを検索し、[ ブランチ/タグの切り替え ] ドロップダウン リストでソースのバージョンを選択します。

Conventions

アプリケーション モデルは、モデルまたはプロバイダー全体をオーバーライドするよりも、モデルの動作をカスタマイズする簡単な方法を提供する規則の抽象化を定義します。 これらの抽象化は、アプリの動作を変更するための推奨される方法です。 規則は、カスタマイズを動的に適用するコードを記述する方法を提供します。 フィルターはフレームワークの動作を変更する手段を提供しますが、カスタマイズによってアプリ全体の動作を制御できます。

次の規則を使用できます。

規則は、MVC オプションに追加するか、属性を実装してコントローラー、アクション、またはアクション パラメーター (フィルターと同様) に適用することによって適用 されます。フィルターとは異なり、規則は、各要求の一部としてではなく、アプリの起動時にのみ実行されます。

Razor Pages ルートとアプリケーション モデル プロバイダー規則については、「ASP.NET Core の Razor Pages ルートとアプリ規則」を参照してください。

ApplicationModelを変更します。

アプリケーション モデルにプロパティを追加するには、次の規則を使用します。

using Microsoft.AspNetCore.Mvc.ApplicationModels;

namespace AppModelSample.Conventions
{
    public class ApplicationDescription : IApplicationModelConvention
    {
        private readonly string _description;

        public ApplicationDescription(string description)
        {
            _description = description;
        }

        public void Apply(ApplicationModel application)
        {
            application.Properties["description"] = _description;
        }
    }
}

MVC が Startup.ConfigureServicesに追加されると、アプリケーション モデル規則がオプションとして適用されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Conventions.Add(new ApplicationDescription("My Application Description"));
        options.Conventions.Add(new NamespaceRoutingConvention());
    });
}

プロパティは、コントローラー アクション内の ActionDescriptor.Properties コレクションからアクセスできます。

public class AppModelController : Controller
{
    public string Description()
    {
        return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
    }
}

ControllerModelの説明を変更する

コントローラー モデルには、カスタム プロパティを含めることもできます。 カスタム プロパティは、アプリケーション モデルで指定された同じ名前で既存のプロパティをオーバーライドします。 次の規則属性は、コントローラー レベルで説明を追加します。

using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;

namespace AppModelSample.Conventions
{
    public class ControllerDescriptionAttribute : Attribute, IControllerModelConvention
    {
        private readonly string _description;

        public ControllerDescriptionAttribute(string description)
        {
            _description = description;
        }

        public void Apply(ControllerModel controllerModel)
        {
            controllerModel.Properties["description"] = _description;
        }
    }
}

この規則は、コントローラーの属性として適用されます。

[ControllerDescription("Controller Description")]
public class DescriptionAttributesController : Controller
{
    public string Index()
    {
        return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
    }

ActionModelの説明を変更する

個別の属性規則を個々のアクションに適用でき、アプリケーションまたはコントローラー レベルで既に適用されている動作をオーバーライドできます。

using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;

namespace AppModelSample.Conventions
{
    public class ActionDescriptionAttribute : Attribute, IActionModelConvention
    {
        private readonly string _description;

        public ActionDescriptionAttribute(string description)
        {
            _description = description;
        }

        public void Apply(ActionModel actionModel)
        {
            actionModel.Properties["description"] = _description;
        }
    }
}

コントローラー内のアクションにこれを適用すると、コントローラー レベルの規則をオーバーライドする方法が示されます。

[ControllerDescription("Controller Description")]
public class DescriptionAttributesController : Controller
{
    public string Index()
    {
        return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
    }

    [ActionDescription("Action Description")]
    public string UseActionDescriptionAttribute()
    {
        return "Description: " + ControllerContext.ActionDescriptor.Properties["description"];
    }
}

ParameterModel を変更します。

アクション パラメーターに次の規則を適用して、 BindingInfoを変更できます。 次の規則では、パラメーターをルート パラメーターにする必要があります。 クエリ文字列値など、他の潜在的なバインディング ソースは無視されます。

using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
using Microsoft.AspNetCore.Mvc.ModelBinding;

namespace AppModelSample.Conventions
{
    public class MustBeInRouteParameterModelConvention : Attribute, IParameterModelConvention
    {
        public void Apply(ParameterModel model)
        {
            if (model.BindingInfo == null)
            {
                model.BindingInfo = new BindingInfo();
            }
            model.BindingInfo.BindingSource = BindingSource.Path;
        }
    }
}

属性は、任意のアクション パラメーターに適用できます。

public class ParameterModelController : Controller
{
    // Will bind:  /ParameterModel/GetById/123
    // WON'T bind: /ParameterModel/GetById?id=123
    public string GetById([MustBeInRouteParameterModelConvention]int id)
    {
        return $"Bound to id: {id}";
    }
}

規則をすべてのアクション パラメーターに適用するには、MustBeInRouteParameterModelConventionMvcOptionsStartup.ConfigureServicesを追加します。

options.Conventions.Add(new MustBeInRouteParameterModelConvention());

ActionModel名を変更する

次の規則では、 ActionModel を変更して、適用先のアクションの 名前 を更新します。 新しい名前は、属性のパラメーターとして指定されます。 この新しい名前はルーティングによって使用されるため、このアクション メソッドに到達するために使用されるルートに影響します。

using System;
using Microsoft.AspNetCore.Mvc.ApplicationModels;

namespace AppModelSample.Conventions
{
    public class CustomActionNameAttribute : Attribute, IActionModelConvention
    {
        private readonly string _actionName;

        public CustomActionNameAttribute(string actionName)
        {
            _actionName = actionName;
        }

        public void Apply(ActionModel actionModel)
        {
            // this name will be used by routing
            actionModel.ActionName = _actionName;
        }
    }
}

この属性は、 HomeControllerのアクション メソッドに適用されます。

// Route: /Home/MyCoolAction
[CustomActionName("MyCoolAction")]
public string SomeName()
{
    return ControllerContext.ActionDescriptor.ActionName;
}

メソッド名が SomeNameされている場合でも、この属性はメソッド名を使用する MVC 規則をオーバーライドし、アクション名を MyCoolActionに置き換えます。 したがって、このアクションに到達するために使用されるルートは /Home/MyCoolAction

このセクションのこの例は、組み込みの ActionNameAttributeを使用する場合と基本的に同じです。

カスタム ルーティング規則

ルーティングのしくみをカスタマイズするには、 IApplicationModelConvention を使用します。 たとえば、次の規則では、コントローラーの名前空間をルートに組み込み、名前空間内の . をルート内の / に置き換えます。

using Microsoft.AspNetCore.Mvc.ApplicationModels;
using System.Linq;

namespace AppModelSample.Conventions
{
    public class NamespaceRoutingConvention : IApplicationModelConvention
    {
        public void Apply(ApplicationModel application)
        {
            foreach (var controller in application.Controllers)
            {
                var hasAttributeRouteModels = controller.Selectors
                    .Any(selector => selector.AttributeRouteModel != null);

                if (!hasAttributeRouteModels
                    && controller.ControllerName.Contains("Namespace")) // affect one controller in this sample
                {
                    // Replace the . in the namespace with a / to create the attribute route
                    // Ex: MySite.Admin namespace will correspond to MySite/Admin attribute route
                    // Then attach [controller], [action] and optional {id?} token.
                    // [Controller] and [action] is replaced with the controller and action
                    // name to generate the final template
                    controller.Selectors[0].AttributeRouteModel = new AttributeRouteModel()
                    {
                        Template = controller.ControllerType.Namespace.Replace('.', '/') + "/[controller]/[action]/{id?}"
                    };
                }
            }

            // You can continue to put attribute route templates for the controller actions depending on the way you want them to behave
        }
    }
}

規則は、 Startup.ConfigureServicesのオプションとして追加されます。

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Conventions.Add(new ApplicationDescription("My Application Description"));
        options.Conventions.Add(new NamespaceRoutingConvention());
    });
}

ヒント

次の方法を使用して、を使用してMvcOptionsに規則を追加します。 {CONVENTION} プレースホルダーは、追加の際に用いる規則です。

services.Configure<MvcOptions>(c => c.Conventions.Add({CONVENTION}));

次の例では、コントローラーが名前に Namespace 属性ルーティングを使用していないルートに規則を適用します。

using Microsoft.AspNetCore.Mvc;

namespace AppModelSample.Controllers
{
    public class NamespaceRoutingController : Controller
    {
        // using NamespaceRoutingConvention
        // route: /AppModelSample/Controllers/NamespaceRouting/Index
        public string Index()
        {
            return "This demonstrates namespace routing.";
        }
    }
}

WebApiCompatShim でのアプリケーションモデルの使用法

ASP.NET Core MVC では、ASP.NET Web API 2 とは異なる規則のセットが使用されます。 カスタム規則を使用すると、ASP.NET Core MVC アプリの動作を Web API アプリの動作と一致するように変更できます。 Microsoft は、特にこの目的のためにWebApiCompatShim NuGet パッケージを出荷します。

ASP.NET Web API からの移行の詳細については、「ASP.NET Web API から ASP.NET Core への移行」を参照してください。

Web API 互換性 Shim を使用するには:

  • Microsoft.AspNetCore.Mvc.WebApiCompatShim パッケージをプロジェクトに追加します。
  • AddWebApiConventionsStartup.ConfigureServicesを呼び出して、MVC に規則を追加します。
services.AddMvc().AddWebApiConventions();

shim によって提供される規則は、特定の属性が適用されているアプリの部分にのみ適用されます。 shim の規則によって規則を変更する必要があるコントローラーを制御するには、次の 4 つの属性を使用します。

アクションの規則

UseWebApiActionConventionsAttribute は、HTTP メソッドを名前に基づいてアクションにマップするために使用されます (たとえば、 GetHttpGet にマップされます)。 属性ルーティングを使用しないアクションにのみ適用されます。

オーバー ロード

UseWebApiOverloadingAttribute は、 WebApiOverloadingApplicationModelConvention 規則を適用するために使用されます。 この規則により、アクションの選択プロセスに OverloadActionConstraint が追加されます。これにより、要求が省略可能でないすべてのパラメーターを満たすアクションに候補アクションが制限されます。

パラメーター規則

UseWebApiParameterConventionsAttribute は、 WebApiParameterConventionsApplicationModelConvention アクション規則を適用するために使用されます。 この規則では、アクション パラメーターとして使用される単純型は既定で URI からバインドされ、複合型は要求本文からバインドされることを指定します。

Routes

UseWebApiRoutesAttribute は、 WebApiApplicationModelConvention コントローラー規則を適用するかどうかを制御します。 この規則を有効にすると、ルートに 領域 のサポートを追加するために使用され、コントローラーが api 領域に含まれていると示されます。

互換性パッケージには、一連の規則に加えて、Web API によって提供されたものを置き換える System.Web.Http.ApiController 基本クラスが含まれています。 これにより、Web API 用に記述され、その ApiController から継承された Web API コントローラーは、ASP.NET Core MVC で実行中に機能します。 前述の UseWebApi* 属性はすべて、基本コントローラー クラスに適用されます。 ApiControllerは、Web API で見つかったプロパティ、メソッド、および結果の型と互換性のある型を公開します。

ApiExplorerを使用してアプリを文書化する

アプリケーション モデルは、アプリの構造を走査するために使用できる各レベルで ApiExplorerModel プロパティを公開します。 これは、 Swagger などのツールを使用して Web API のヘルプ ページを生成するために使用できます。 ApiExplorer プロパティは、アプリのモデルのどの部分を公開するかを指定するために設定できるIsVisible プロパティを公開します。 規則を使用して、この設定を構成します。

using Microsoft.AspNetCore.Mvc.ApplicationModels;

namespace AppModelSample.Conventions
{
    public class EnableApiExplorerApplicationConvention : IApplicationModelConvention
    {
        public void Apply(ApplicationModel application)
        {
            application.ApiExplorer.IsVisible = true;
        }
    }
}

この方法 (および必要に応じて追加の規則) を使用すると、API の可視性はアプリ内の任意のレベルで有効または無効になります。