Utiliser le modèle d’application dans ASP.NET Core

Par Steve Smith

ASP.NET Core MVC définit un modèle d’application représentant les composants d’une application MVC. Lire et manipuler ce modèle pour modifier le comportement des éléments MVC. Par défaut, MVC suit certaines conventions pour déterminer quelles classes sont considérées comme des contrôleurs, quelles méthodes sur ces classes sont des actions, et comment les paramètres et le routage se comportent. Personnalisez ce comportement en fonction des besoins d’une application en créant des conventions personnalisées et en les appliquant globalement ou en tant qu’attributs.

Modèles et fournisseurs (IApplicationModelProvider)

Le modèle d’application ASP.NET Core MVC inclut des interfaces abstraites et des classes d’implémentation concrètes qui décrivent une application MVC. Ce modèle est le résultat de la découverte par MVC des contrôleurs, des actions, des paramètres d’action, des routes et des filtres de l’application en fonction de conventions par défaut. En utilisant le modèle d’application, modifiez une application pour suivre des conventions différentes du comportement de MVC par défaut. Les paramètres, les noms, les routes et les filtres sont utilisés comme données de configuration pour les actions et les contrôleurs.

Le modèle d’application ASP.NET Core MVC a la structure suivante :

  • ApplicationModel
    • Contrôleurs (ControllerModel)
      • Actions (ActionModel)
        • Paramètres (ParameterModel)

Chaque niveau du modèle a accès à une collection Properties commune, et les niveaux inférieurs peuvent accéder à et remplacer les valeurs de propriétés définies par les niveaux supérieurs de la hiérarchie. Les propriétés sont rendues persistantes dans ActionDescriptor.Properties quand les actions sont créées. Ensuite, quand une requête est traitée, toutes les propriétés ajoutées ou modifiées par une convention sont accessibles via ActionContext.ActionDescriptor. L’utilisation de propriétés est un bon moyen de configurer des filtres, des classeurs de modèles et d’autres aspects d’un modèle d’application à l’échelle d’une action.

Notes

La collection ActionDescriptor.Properties n’est pas thread-safe (pour les écritures) après le démarrage de l’application. Les conventions sont la meilleure façon d’ajouter des données de façon sécurisée à cette collection.

ASP.NET Core MVC charge le modèle d’application à l’aide d’un modèle de fournisseur, défini par l’interface IApplicationModelProvider. Cette section aborde certains aspects de l’implémentation interne concernant la façon dont fonctionne de ce fournisseur. L’utilisation du modèle de fournisseur est un sujet avancé, principalement pour l’utilisation de l’infrastructure. La plupart des applications doivent utiliser des conventions, et non le modèle de fournisseur.

Les implémentations de l’interface IApplicationModelProvider sont « enveloppées » les unes dans les autres, chaque implémentation appelant OnProvidersExecuting dans un ordre croissant en fonction de sa propriété Order. La méthode OnProvidersExecuted est ensuite appelée dans l’ordre inverse. Le framework définit plusieurs fournisseurs :

D’abord (Order=-1000) :

  • DefaultApplicationModelProvider

Ensuite (Order=-990) :

  • AuthorizationApplicationModelProvider
  • CorsApplicationModelProvider

Notes

L’ordre dans lequel deux fournisseurs ayant la même valeur de Order sont appelés n’est pas défini et vous ne pouvez pas vous baser sur celui-ci.

Notes

IApplicationModelProvider est un concept avancé que les auteurs de framework peuvent étendre. D’une façon générale, les applications doivent utiliser des conventions et les infrastructures doivent utiliser des fournisseurs. La principale différence est que les fournisseurs s’exécutent toujours avant les conventions.

DefaultApplicationModelProvider établit la plupart des comportements par défaut utilisés par ASP.NET Core MVC. Ses responsabilités incluent :

  • L’ajout de filtres globaux au contexte
  • L’ajout de contrôleurs au contexte
  • L’ajout de méthodes publiques de contrôleur en tant qu’actions
  • L’ajout de paramètres de méthode d’action au contexte
  • L’application de routes et d’autres attributs

Certains comportements intégrés sont implémentées par le DefaultApplicationModelProvider. Ce fournisseur est responsable de la construction du ControllerModel, qui à son tour fait référence aux instances ActionModel, PropertyModelet ParameterModel. La classe DefaultApplicationModelProvider est un détail d’implémentation interne de l’infrastructure qui peut changer dans le futur.

Le AuthorizationApplicationModelProvider est responsable de l’application du comportement associé aux attributs AuthorizeFilter et AllowAnonymousFilter. Pour plus d’informations, consultez Autorisation simple dans ASP.NET Core.

CorsApplicationModelProvider implémente le comportement associé à IEnableCorsAttribute et à IDisableCorsAttribute. Pour plus d’informations, consultez Activer les requêtes cross-origin (CORS) dans ASP.NET Core.

Les informations relatives aux fournisseurs internes de l’infrastructure décrites dans cette section ne sont pas disponibles via le navigateur de l’API .NET. Toutefois, les fournisseurs peuvent être consultés dans la source de référence ASP.NET Core (dépôt GitHub .NET/ASP.NET). Utilisez la recherche GitHub pour rechercher les fournisseurs par nom et sélectionnez la version de la source avec la liste déroulante Changer de branches/d’étiquettes.

Conventions

Le modèle d’application définit des abstractions de convention qui fournissent un moyen de personnaliser le comportement des modèles plus simple que de remplacer tout le modèle ou tout le fournisseur. Ces abstractions constituent le moyen recommandé pour modifier le comportement d’une application. Les conventions fournissent un moyen d’écrire du code qui applique dynamiquement des personnalisations. Alors que les filtres fournissent un moyen de modifier le comportement de l’infrastructure, les personnalisations permettent de contrôler comment l’ensemble de l’application fonctionne.

Les conventions suivantes sont disponibles :

Les conventions sont appliquées en les ajoutant aux options MVC ou en implémentant des attributs et en les appliquant aux contrôleurs, aux actions ou aux paramètres d’action (similaires aux filtres). Contrairement aux filtres, les conventions sont exécutées uniquement lorsque l’application démarre, et non dans le cadre de chaque requête.

Notes

Pour plus d’informations sur les conventions de fournisseur de modèles d’application et d’itinéraire de Razor Pages, consultez Conventions d’application et d’itinéraire de Razor Pages dans ASP.NET Core.

Modifier ApplicationModel

La convention suivante est utilisée pour ajouter une propriété au modèle d’application :

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;
        }
    }
}

Les conventions de modèle d’application sont appliquées en tant qu’options quand MVC est ajouté à Startup.ConfigureServices :

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

Les propriétés sont accessibles à partir de la collection ActionDescriptor.Properties dans les actions de contrôleur :

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

Modifier la description de ControllerModel

Le modèle de contrôleur peut également inclure des propriétés personnalisées. Les propriétés personnalisées remplacent les propriétés existantes portant le même nom que celui spécifié dans le modèle d’application. L’attribut de convention suivant ajoute une description au niveau du contrôleur :

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;
        }
    }
}

Cette convention est appliquée en tant qu’attribut sur un contrôleur :

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

Modifier la description de ActionModel

Une convention d’attribut distincte peut être appliquée à des actions individuelles, en remplaçant le comportement déjà appliqué au niveau de l’application ou du contrôleur :

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;
        }
    }
}

L’application de ceci à une action dans le contrôleur montre comment elle remplace la convention au niveau du contrôleur :

[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"];
    }
}

Modifier ParameterModel

La convention suivante peut être appliquée à des paramètres d’action pour modifier leur BindingInfo. La convention suivante nécessite que le paramètre soit un paramètre d’itinéraire. D’autres sources de liaison potentielles, telles que les valeurs de chaîne de requête, sont ignorées :

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;
        }
    }
}

L’attribut peut être appliqué à n’importe quel paramètre d’action :

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}";
    }
}

Pour appliquer la convention à tous les paramètres d’action, ajoutez MustBeInRouteParameterModelConvention à MvcOptions dans Startup.ConfigureServices :

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

Modifier le nom d’ActionModel

La convention suivante modifie le ActionModel pour mettre à jour le nom de l’action à laquelle il est appliqué. Le nouveau nom est fourni à l’attribut en tant que paramètre. Ce nouveau nom est utilisé par le routage, il affecte donc l’itinéraire utilisé pour atteindre cette méthode d’action :

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;
        }
    }
}

Cet attribut est appliqué à une méthode d’action dans le HomeController :

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

Même si le nom de la méthode est SomeName, l’attribut remplace la convention MVC d’utiliser le nom de méthode et remplace le nom de l’action par MyCoolAction. Ainsi, la route utilisée pour atteindre cette action est /Home/MyCoolAction.

Notes

L’exemple de cette section est essentiellement identique à l’utilisation de l’ActionNameAttribute intégré.

Convention de routage personnalisée

Utilisez IApplicationModelConvention pour personnaliser le fonctionnement du routage. Par exemple, la convention suivante intègre des espaces de noms de contrôleurs dans leurs itinéraires, en remplaçant . dans l’espace de noms par / dans l’itinéraire :

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
        }
    }
}

La convention est ajoutée en tant qu’option dans Startup.ConfigureServices :

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

Conseil

Ajoutez des conventions à l’intergiciel via MvcOptions à l’aide de l’approche suivante. L’espace réservé {CONVENTION} est la convention à ajouter :

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

L’exemple suivant applique cette convention aux itinéraires qui n’utilisent pas le routage d’attribut dont le nom de contrôleur contient 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.";
        }
    }
}

Utilisation du modèle d’application dans WebApiCompatShim

ASP.NET Core MVC utilise un autre ensemble de conventions d’ASP.NET Web API 2. À l’aide de conventions personnalisées, vous pouvez modifier le comportement d’une application ASP.NET Core MVC pour qu’il soit cohérent avec celui d’une application d’API web. Microsoft livre , un WebApiCompatShimpackage NuGet, spécialement à cette fin.

Notes

Pour plus d’informations sur la migration à partir de l’API Web ASP.NET, consultez Migrer de l’API Web ASP.NET vers ASP.NET Core.

Pour utiliser le shim de compatibilité de l’API Web :

  • Ajoutez le package Microsoft.AspNetCore.Mvc.WebApiCompatShim au projet.
  • Ajoutez les conventions à MVC en appelant AddWebApiConventions dans Startup.ConfigureServices :
services.AddMvc().AddWebApiConventions();

Les conventions fournies par le shim sont appliquées seulement aux parties de l’application auxquelles certains attributs sont appliqués. Les quatre attributs suivants sont utilisés pour spécifier quels contrôleurs doivent avoir leurs conventions modifiées par les conventions du shim :

Conventions d’actions

UseWebApiActionConventionsAttribute est utilisé pour mapper la méthode HTTP à des actions en fonction de leur nom (par exemple, Get est mappé à HttpGet). Il s’applique seulement aux actions qui n’utilisent pas le routage par attributs.

Surcharge

UseWebApiOverloadingAttribute est utilisé pour appliquer la convention WebApiOverloadingApplicationModelConvention. Cette convention ajoute une OverloadActionConstraint au processus de sélection d’actions, qui limite les actions candidates à celles pour lesquelles la requête satisfait à tous les paramètres non facultatifs.

Conventions de paramètres

UseWebApiParameterConventionsAttribute est utilisé pour appliquer la convention d’actions WebApiParameterConventionsApplicationModelConvention. Cette convention spécifie que des types simples utilisés comme paramètres d’actions sont liés à partir de l’URI par défaut, alors que les types complexes sont liés à partir du corps de la requête.

Itinéraires

UseWebApiRoutesAttribute contrôle si la convention de contrôleur WebApiApplicationModelConvention est appliquée. Lorsqu’elle est activée, cette convention est utilisée pour ajouter la prise en charge des zones pour l’itinéraire et indique que le contrôleur se trouve dans la zone api.

En plus d’un ensemble de conventions, le package de compatibilité inclut une classe de base System.Web.Http.ApiController qui remplace celle fournie par l’API web. Ceci permet aux contrôleurs de votre API web écrits pour l’API web et héritant de son ApiController de fonctionner quand ils s’exécutent sur ASP.NET Core MVC. Tous les attributs UseWebApi* répertoriés précédemment sont appliqués à la classe de contrôleur de base. ApiController expose les propriétés, les méthodes et les types de résultats qui sont compatibles avec ceux trouvés dans l’API web.

Utiliser ApiExplorer pour documenter une application

Le modèle d’application expose une propriété ApiExplorerModel à chaque niveau qui peut être utilisé pour parcourir la structure de l’application. Ceci peut être utilisé pour générer des pages d’aide sur des API web à l’aide d’outils comme Swagger. La propriété ApiExplorer expose une propriété IsVisible qui peut être définie pour spécifier les parties du modèle d’application qui doivent être exposées. Configurez ce paramètre en utilisant une convention :

using Microsoft.AspNetCore.Mvc.ApplicationModels;

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

Avec cette approche (et avec des conventions supplémentaires si nécessaire), la visibilité de l’API est activée ou désactivée à n’importe quel niveau au sein d’une application.