Condividi tramite


Usare il modello di applicazione in ASP.NET Core

Di Steve Smith

ASP.NET Core MVC definisce un modello di applicazione che rappresenta i componenti di un'app MVC. Leggere e modificare questo modello per modificare il comportamento degli elementi MVC. Per impostazione predefinita, MVC segue determinate convenzioni per determinare quali classi sono considerate controller, quali metodi su tali classi sono azioni e come si comportano i parametri e il routing. Personalizzare questo comportamento in base alle esigenze di un'app creando convenzioni personalizzate e applicandole a livello globale o come attributi.

Modelli e provider (IApplicationModelProvider)

Il modello di applicazione MVC core ASP.NET include sia interfacce astratte che classi di implementazione concrete che descrivono un'applicazione MVC. Questo modello è il risultato dell'individuazione di controller, azioni, parametri di azione, route e filtri dell'app in base alle convenzioni predefinite. Usando il modello di applicazione, modificare un'app in modo da seguire convenzioni diverse rispetto al comportamento MVC predefinito. I parametri, i nomi, le route e i filtri vengono tutti usati come dati di configurazione per azioni e controller.

Il modello di applicazione MVC core ASP.NET ha la struttura seguente:

  • ApplicationModel
    • Controller (ControllerModel)
      • Azioni (ActionModel)
        • Parametri (ParameterModel)

Ogni livello del modello ha accesso a una raccolta comune Properties e i livelli inferiori possono accedere e sovrascrivere i valori delle proprietà impostati da livelli più elevati nella gerarchia. Le proprietà vengono mantenute nell'oggetto ActionDescriptor.Properties quando vengono create le azioni. Quando una richiesta viene gestita, è possibile accedere a qualsiasi proprietà che una convenzione ha aggiunto o modificato tramite ActionContext.ActionDescriptor. L'uso delle proprietà è un ottimo modo per configurare filtri, associatori di modelli e altri aspetti del modello di app in base all'azione.

Annotazioni

La raccolta ActionDescriptor.Properties non è thread-safe per le operazioni di scrittura dopo l'avvio dell'app. Le convenzioni sono il modo migliore per aggiungere in modo sicuro i dati a questa raccolta.

ASP.NET Core MVC carica il modello di applicazione usando un modello di provider, definito dall'interfaccia IApplicationModelProvider . Questa sezione illustra alcuni dettagli dell'implementazione interna del funzionamento di questo provider. L'uso del modello di provider è un oggetto avanzato, principalmente per l'uso del framework. La maggior parte delle app deve usare convenzioni, non il modello di provider.

Implementazioni dell'interfaccia IApplicationModelProvider incapsulano una l'altra, in cui ogni implementazione chiama OnProvidersExecuting in ordine crescente in base alla sua proprietà Order. Il OnProvidersExecuted metodo viene quindi chiamato in ordine inverso. Il framework definisce diversi provider:

Primo (Order=-1000):

  • DefaultApplicationModelProvider

Quindi (Order=-990):

  • AuthorizationApplicationModelProvider
  • CorsApplicationModelProvider

Annotazioni

L'ordine in cui vengono chiamati due provider con lo stesso valore per Order è indefinito e non deve essere considerato attendibile.

Annotazioni

IApplicationModelProvider è un concetto avanzato per gli autori di framework da estendere. In generale, le app devono usare convenzioni e i framework devono usare provider. La distinzione principale è che i provider vengono sempre eseguiti prima delle convenzioni.

DefaultApplicationModelProvider Stabilisce molti dei comportamenti predefiniti usati da ASP.NET Core MVC. Le sue responsabilità includono:

  • Aggiunta di filtri globali al contesto
  • Aggiunta di controller al contesto
  • Aggiunta di metodi del controller pubblico come azioni
  • aggiunta di parametri del metodo d'azione al contesto
  • Applicazione di percorsi e altri attributi

Alcuni comportamenti predefiniti vengono implementati da DefaultApplicationModelProvider. Questo provider è responsabile della costruzione di ControllerModel, che a sua volta fa riferimento alle istanze ActionModel, PropertyModel e ParameterModel. La DefaultApplicationModelProvider classe è un dettaglio di implementazione del framework interno che potrebbe cambiare in futuro.

AuthorizationApplicationModelProvider è responsabile dell'applicazione del comportamento associato agli attributi AuthorizeFilter e AllowAnonymousFilter. Per altre informazioni, vedere Autorizzazione semplice in ASP.NET Core.

CorsApplicationModelProvider implementa il comportamento associato a IEnableCorsAttribute e IDisableCorsAttribute. Per altre informazioni, vedere Abilitare le richieste tra le origini (CORS) in ASP.NET Core.

Le informazioni sui provider interni del framework descritti in questa sezione non sono disponibili tramite il browser api .NET. Tuttavia, i provider possono essere esaminati nel codice sorgente di riferimento ASP.NET Core (repository GitHub dotnet/aspnetcore). Usa la ricerca di GitHub per trovare i provider per nome e selezionare la versione della sorgente con l'elenco a discesa Cambia rami/tag.

Convenzioni

Il modello applicativo definisce astrazioni di convenzioni che offrono un modo più semplice per personalizzare il comportamento dei modelli rispetto all'override dell'intero modello o provider. Queste astrazioni sono il modo consigliato per modificare il comportamento di un'app. Le convenzioni consentono di scrivere codice che applica in modo dinamico le personalizzazioni. Mentre i filtri consentono di modificare il comportamento del framework, le personalizzazioni consentono di controllare il funzionamento dell'intera app.

Sono disponibili le convenzioni seguenti:

Le convenzioni vengono applicate aggiungendole alle opzioni MVC o implementando attributi e applicandoli a controller, azioni o parametri di azione (simili ai filtri). A differenza dei filtri, le convenzioni vengono eseguite solo all'avvio dell'app, non come parte di ogni richiesta.

Annotazioni

Per informazioni sulle convenzioni del provider di route Pages e del modello di applicazione, vedere Razor.

Modificare l'oggetto ApplicationModel

La convenzione seguente viene usata per aggiungere una proprietà al modello di applicazione:

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

Le convenzioni del modello di applicazione vengono applicate come opzioni quando MVC viene aggiunto in Startup.ConfigureServices:

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

Le proprietà sono accessibili dalla raccolta ActionDescriptor.Properties all'interno delle azioni del controller:

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

Modificare la ControllerModel descrizione

Il modello di controller può includere anche proprietà personalizzate. Le proprietà personalizzate sostituiscono le proprietà esistenti con lo stesso nome specificato nel modello di applicazione. L'attributo convenzione seguente aggiunge una descrizione a livello di controller:

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

Questa convenzione viene applicata come attributo in un controller:

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

Modificare la ActionModel descrizione

È possibile applicare una convenzione di attributo separata alle singole azioni, sostituendo il comportamento già applicato a livello di applicazione o controller:

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'applicazione a un'azione all'interno del controller dimostra come esegue l'override della convenzione a livello di controller:

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

Modificare l'oggetto ParameterModel

La convenzione seguente può essere applicata ai parametri azione per modificarne BindingInfo. La convenzione seguente richiede che il parametro sia un parametro di route. Altre origini di associazione potenziali, ad esempio i valori della stringa di query, vengono ignorate:

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'attributo può essere applicato a qualsiasi parametro di azione:

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

Per applicare la convenzione a tutti i parametri di azione, aggiungere il MustBeInRouteParameterModelConvention a MvcOptions in Startup.ConfigureServices:

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

Modificare il ActionModel nome

La convenzione seguente modifica l'oggetto ActionModel per aggiornare il nome dell'azione a cui viene applicata. Il nuovo nome viene fornito come parametro per l'attributo . Questo nuovo nome viene usato dal routing, quindi influisce sulla route usata per raggiungere questo metodo di azione:

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

Questo attributo viene applicato a un metodo di azione in HomeController:

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

Anche se il nome del metodo è SomeName, l'attributo esegue l'override della convenzione MVC di usando il nome del metodo e sostituisce il nome dell'azione con MyCoolAction. Di conseguenza, la route usata per raggiungere questa azione è /Home/MyCoolAction.

Annotazioni

Questo esempio nella presente sezione è essenzialmente uguale all'uso dell'oggetto integrato ActionNameAttribute.

Convenzione di routing personalizzata

Usare un IApplicationModelConvention per personalizzare il funzionamento del routing. Ad esempio, la convenzione seguente incorpora gli spazi dei nomi dei controller nelle route, sostituendo . nello spazio dei nomi con / nella route:

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 convenzione viene aggiunta come opzione in Startup.ConfigureServices:

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

Suggerimento

Aggiungere convenzioni al middleware attraverso MvcOptions seguendo l'approccio seguente. Il {CONVENTION} segnaposto è la convenzione da aggiungere:

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

L'esempio seguente applica una convenzione alle route che non usano il routing degli attributi in cui il nome del controller contiene 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.";
        }
    }
}

Utilizzo del modello di applicazione in WebApiCompatShim

ASP.NET Core MVC usa un set di convenzioni diverso da ASP.NET API Web 2. Usando convenzioni personalizzate, è possibile modificare il comportamento di un'app MVC core ASP.NET in modo che sia coerente con quello di un'app per le API Web. Microsoft fornisce il WebApiCompatShim pacchetto NuGet in modo specifico a questo scopo.

Annotazioni

Per altre informazioni sulla migrazione dall'API Web ASP.NET, vedere Eseguire la migrazione dall'API Web ASP.NET a ASP.NET Core.

Per utilizzare lo shim di compatibilità della Web API:

  • Aggiungere il pacchetto Microsoft.AspNetCore.Mvc.WebApiCompatShim al progetto.
  • Aggiungere le convenzioni a MVC chiamando AddWebApiConventions in Startup.ConfigureServices:
services.AddMvc().AddWebApiConventions();

Le convenzioni fornite dallo shim vengono applicate solo alle parti dell'app a cui sono stati applicati determinati attributi. I seguenti quattro attributi vengono utilizzati per controllare quali controller devono avere le loro convenzioni modificate dalle convenzioni dello shim.

Convenzioni di azione

UseWebApiActionConventionsAttribute viene usato per mappare il metodo HTTP alle azioni in base al nome (ad esempio, Get viene mappato a HttpGet). Si applica solo alle azioni che non usano il routing degli attributi.

Sovraccarico

UseWebApiOverloadingAttribute viene usato per applicare la WebApiOverloadingApplicationModelConvention convenzione. Questa convenzione aggiunge un oggetto OverloadActionConstraint al processo di selezione delle azioni, che limita le azioni candidate a quelle per cui la richiesta soddisfa tutti i parametri non facoltativi.

Convenzioni dei parametri

UseWebApiParameterConventionsAttribute viene usato per applicare la convenzione di WebApiParameterConventionsApplicationModelConvention azione. Questa convenzione specifica che i tipi semplici usati come parametri di azione sono associati dall'URI per impostazione predefinita, mentre i tipi complessi sono associati dal corpo della richiesta.

Percorsi

UseWebApiRoutesAttribute controlla se viene applicata la WebApiApplicationModelConvention convenzione del controller. Se abilitata, questa convenzione viene usata per aggiungere il supporto per le aree alla route e indica che il controller si trova nell'area api .

Oltre a un set di convenzioni, il pacchetto di compatibilità include una System.Web.Http.ApiController classe base che sostituisce quella fornita dall'API Web. In questo modo, i controller delle API web scritti per l'API web e che ereditano dall'elemento ApiController, possono funzionare durante l'esecuzione su ASP.NET Core MVC. Tutti gli UseWebApi* attributi elencati in precedenza vengono applicati alla classe controller di base. ApiController espone proprietà, metodi e tipi di risultati compatibili con quelli presenti nell'API Web.

Usare ApiExplorer per documentare un'app

Il modello di applicazione espone una ApiExplorerModel proprietà a ogni livello che può essere usata per attraversare la struttura dell'app. Può essere usato per generare pagine della guida per le API web usando strumenti come Swagger. La ApiExplorer proprietà espone una IsVisible proprietà che può essere impostata per specificare quali parti del modello dell'app devono essere esposte. Configurare questa impostazione usando una convenzione:

using Microsoft.AspNetCore.Mvc.ApplicationModels;

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

Usando questo approccio (e convenzioni aggiuntive, se necessario), la visibilità dell'API è abilitata o disabilitata a qualsiasi livello all'interno di un'app.