Usare il modello applicativo in ASP.NET Core
Di Steve Smith
ASP.NET Core MVC definisce un modello applicativo 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 del rilevamento da parte di MVC 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 usati come dati di configurazione per le azioni e controller.
Il modello applicativo ASP.NET Core MVC presenta la struttura seguente:
- ApplicationModel
- Controller (ControllerModel)
- Azioni (ActionModel)
- Parametri (ParameterModel)
- Azioni (ActionModel)
- Controller (ControllerModel)
Ogni livello del modello ha accesso a una raccolta Properties
comune. I livelli inferiori possono accedere e sovrascrivere i valori di proprietà impostati dai livelli superiori nella gerarchia. Le proprietà vengono rese persistenti in ActionDescriptor.Properties quando vengono create le azioni. In seguito, quando viene gestita una richiesta, le proprietà aggiunte o modificate da una convenzione sono accessibili 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.
Nota
La ActionDescriptor.Properties raccolta non è thread-safe (per le scritture) dopo l'avvio dell'app. Le convenzioni sono il metodo migliore per aggiungere dati in modo sicuro a questa raccolta.
ASP.NET Core MVC carica il modello di applicazione usando un modello di provider, definito dall'interfaccia IApplicationModelProvider . La presente sezione illustra alcuni dettagli di implementazione interna associati al 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 "a capo" l'una l'altra IApplicationModelProvider , in cui ogni implementazione chiama OnProvidersExecuting in ordine crescente in base alla relativa Order proprietà. Il metodo OnProvidersExecuted viene quindi chiamato in ordine inverso. Il framework definisce diversi provider:
First (Order=-1000
):
DefaultApplicationModelProvider
Then (Order=-990
):
AuthorizationApplicationModelProvider
CorsApplicationModelProvider
Nota
L'ordine in cui vengono chiamati due provider con lo stesso valore per Order
è indefinito e non deve essere considerato attendibile.
Nota
IApplicationModelProvider è un concetto avanzato che gli autori di framework potranno estendere. In generale, le app devono usare convenzioni e i framework devono usare provider. La differenza principale è il fatto che i provider vengono sempre eseguiti prima delle convenzioni.
DefaultApplicationModelProvider
definisce molti comportamenti predefiniti usati da ASP.NET Core MVC. Le responsabilità includono:
- Aggiunta di filtri globali al contesto
- Aggiunta di controller al contesto
- Aggiunta di metodi del controller pubblici come azioni
- Aggiunta di parametri del metodo di azione al contesto
- Applicazione di route 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.
Implementa CorsApplicationModelProvider
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 nell'origine di riferimento ASP.NET Core (repository GitHub dotnet/aspnetcore). Usare la ricerca in GitHub per trovare i provider in base al nome e selezionare la versione dell'origine con l'elenco a discesa Cambia rami/tag .
Convenzioni
Il modello applicativo definisce astrazioni di convenzioni che offrono un metodo di personalizzazione del comportamento dei modelli più semplice 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:
- IApplicationModelConvention
- IControllerModelConvention
- IActionModelConvention
- IParameterModelConvention
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.
Nota
Per informazioni sulle convenzioni del Razor provider di route Pages e del modello di applicazione, vedere Razor Convenzioni di route e app pages in ASP.NET Core.
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 all'interno delle azioni del ActionDescriptor.Properties 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 di convenzione seguente aggiunge una descrizione a livello del 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
È possibile applicare la convenzione seguente a parametri di azione per modificarne l'elemento 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 a MustBeInRouteParameterModelConvention
MvcOptions in Startup.ConfigureServices
:
options.Conventions.Add(new MustBeInRouteParameterModelConvention());
Modificare il ActionModel
nome
La convenzione seguente modifica ActionModel per aggiornare l'elemento name dell'azione alla quale viene applicata. Il nuovo nome viene passato all'attributo come parametro. 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 che prevede l'uso del nome del metodo e sostituisce il nome dell'azione con MyCoolAction
. Pertanto la route usata per raggiungere questa azione è /Home/MyCoolAction
.
Nota
Questo esempio in questa sezione è essenzialmente uguale all'uso dell'oggetto predefinito ActionNameAttribute.
Convenzione di routing personalizzata
Usare un IApplicationModelConvention oggetto 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 usando MvcOptions 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 controller ha Namespace
il nome:
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 rispetto all'API Web ASP.NET 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.
Nota
Per altre informazioni sulla migrazione da API Web ASP.NET, vedere Eseguire la migrazione da API Web ASP.NET a ASP.NET Core.
Per usare lo shim di compatibilità dell'API Web:
- Aggiungere il pacchetto
Microsoft.AspNetCore.Mvc.WebApiCompatShim
al progetto. - Aggiungere le convenzioni a MVC chiamando AddWebApiConventions in
Startup.ConfigureServices
:
services.AddMvc().AddWebApiConventions();
Le convenzioni offerte dallo shim vengono applicate solo alle parti dell'app a cui sono applicati determinati attributi. I quattro attributi seguenti vengono usati per determinare i controller le cui convenzioni verranno modificate dalle convenzioni dello shim:
- UseWebApiActionConventionsAttribute
- UseWebApiOverloadingAttribute
- UseWebApiParameterConventionsAttribute
- UseWebApiRoutesAttribute
Convenzioni di azione
UseWebApiActionConventionsAttribute viene usato per eseguire il mapping del metodo HTTP alle azioni in base al nome ( ad esempio, Get
viene eseguito il mapping a HttpGet
). Si applica solo alle azioni che non usano il routing degli attributi.
Overload
UseWebApiOverloadingAttribute viene usato per applicare la WebApiOverloadingApplicationModelConvention convenzione. Questa convenzione aggiunge un elemento OverloadActionConstraint al processo di selezione 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.
Route
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 API Web scritti per l'API Web e ereditano dal relativo ApiController
funzionamento durante l'esecuzione in 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 applicativo espone a ogni livello una proprietà ApiExplorerModel 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.