Praca z modelem aplikacji w programie ASP.NET Core

Autor: Steve Smith

ASP.NET Core MVC definiuje model aplikacji reprezentujący składniki aplikacji MVC. Odczyt i manipulowanie tym modelem w celu zmodyfikowania sposobu zachowania elementów MVC. Domyślnie MVC jest zgodna z niektórymi konwencjami, aby określić, które klasy są traktowane jako kontrolery, które metody w tych klasach są akcjami, oraz jak zachowują się parametry i routing. Dostosuj to zachowanie, aby odpowiadało potrzebom aplikacji, tworząc konwencje niestandardowe i stosując je globalnie lub jako atrybuty.

Modele i dostawcy (IApplicationModelProvider)

Model aplikacji ASP.NET Core MVC zawiera zarówno abstrakcyjne interfejsy, jak i konkretne klasy implementacji opisujące aplikację MVC. Ten model jest wynikiem odnajdywania kontrolerów aplikacji, akcji, parametrów akcji, tras i filtrów zgodnie z konwencjami domyślnymi. Pracując z modelem aplikacji, zmodyfikuj aplikację, aby postępowała zgodnie z różnymi konwencjami niż domyślne zachowanie MVC. Parametry, nazwy, trasy i filtry są używane jako dane konfiguracji dla akcji i kontrolerów.

Model aplikacji platformy ASP.NET Core MVC ma następującą strukturę:

  • ApplicationModel
    • Kontrolery (ControllerModel)
      • Akcje (ActionModel)
        • Parametry (ParameterModel)

Każdy poziom modelu ma dostęp do wspólnej Properties kolekcji, a niższe poziomy mogą uzyskiwać dostęp do wartości właściwości ustawionych przez wyższe poziomy w hierarchii i zastępować je. Właściwości są utrwalane podczas ActionDescriptor.Properties tworzenia akcji. Następnie po obsłużeniu żądania wszystkie właściwości, do których można uzyskać dostęp za pośrednictwem polecenia , do każdej konwencji dodanej lub zmodyfikowanej.ActionContext.ActionDescriptor Korzystanie z właściwości to doskonały sposób konfigurowania filtrów, powiązań modelu i innych aspektów modelu aplikacji na podstawie poszczególnych akcji.

Uwaga

Kolekcja ActionDescriptor.Properties nie jest bezpieczna wątkiem (w przypadku zapisów) po uruchomieniu aplikacji. Konwencje to najlepszy sposób bezpiecznego dodawania danych do tej kolekcji.

ASP.NET Core MVC ładuje model aplikacji przy użyciu wzorca dostawcy zdefiniowanego IApplicationModelProvider przez interfejs. W tej sekcji opisano niektóre szczegóły implementacji wewnętrznej dotyczące sposobu działania tego dostawcy. Korzystanie ze wzorca dostawcy jest zaawansowanym tematem, głównie do użycia ze strukturą. Większość aplikacji powinna używać konwencji, a nie wzorca dostawcy.

Implementacje interfejsu IApplicationModelProvider "zawijają się" nawzajem, gdzie każda implementacja wywołuje OnProvidersExecuting w kolejności rosnącej na podstawie jego Order właściwości. Następnie metoda jest wywoływana OnProvidersExecuted w odwrotnej kolejności. Struktura definiuje kilku dostawców:

Pierwszy (Order=-1000):

  • DefaultApplicationModelProvider

Następnie (Order=-990):

  • AuthorizationApplicationModelProvider
  • CorsApplicationModelProvider

Uwaga

Kolejność wywoływana przez dwóch dostawców o tej samej wartości Order jest niezdefiniowana i nie powinna być zależna.

Uwaga

IApplicationModelProvider to zaawansowana koncepcja rozszerzeń dla autorów struktur. Ogólnie rzecz biorąc, aplikacje powinny używać konwencji, a struktury powinny używać dostawców. Kluczowym rozróżnieniem jest to, że dostawcy zawsze uruchamiają się przed konwencjami.

Ustawienie DefaultApplicationModelProvider określa wiele domyślnych zachowań używanych przez ASP.NET Core MVC. Jego obowiązki obejmują:

  • Dodawanie filtrów globalnych do kontekstu
  • Dodawanie kontrolerów do kontekstu
  • Dodawanie metod kontrolera publicznego jako akcji
  • Dodawanie parametrów metody akcji do kontekstu
  • Stosowanie trasy i innych atrybutów

Niektóre wbudowane zachowania są implementowane przez element DefaultApplicationModelProvider. Ten dostawca jest odpowiedzialny za konstruowanie ControllerModelobiektu , który z kolei odwołuje się do ActionModelwystąpień , PropertyModeli ParameterModel . Klasa DefaultApplicationModelProvider to wewnętrzny szczegół implementacji struktury, który może ulec zmianie w przyszłości.

Element AuthorizationApplicationModelProvider jest odpowiedzialny za zastosowanie zachowania skojarzonego z atrybutami AuthorizeFilter i AllowAnonymousFilter . Aby uzyskać więcej informacji, zobacz Simple authorization in ASP.NET Core (Prosta autoryzacja w programie ASP.NET Core).

Implementuje CorsApplicationModelProvider zachowanie skojarzone z elementami IEnableCorsAttribute i IDisableCorsAttribute. Aby uzyskać więcej informacji, zobacz Włączanie żądań między źródłami (CORS) w ASP.NET Core.

Informacje na temat dostawców wewnętrznych platformy opisanych w tej sekcji nie są dostępne za pośrednictwem przeglądarki interfejsu API platformy .NET. Jednak dostawcy mogą być sprawdzani w źródle referencyjnym platformy ASP.NET Core (repozytorium dotnet/aspnetcore w usłudze GitHub). Użyj wyszukiwania w usłudze GitHub, aby znaleźć dostawców według nazwy i wybrać wersję źródła z listą rozwijaną Przełącz gałęzie/tagi .

Konwencje

Model aplikacji definiuje abstrakcje konwencji, które zapewniają prostszy sposób dostosowywania zachowania modeli niż zastępowanie całego modelu lub dostawcy. Te abstrakcje są zalecanym sposobem modyfikowania zachowania aplikacji. Konwencje umożliwiają pisanie kodu, który dynamicznie stosuje dostosowania. Chociaż filtry zapewniają sposób modyfikowania zachowania platformy, dostosowania umożliwiają kontrolę nad tym, jak działa cała aplikacja.

Dostępne są następujące konwencje:

Konwencje są stosowane przez dodanie ich do opcji MVC lub przez zaimplementowanie atrybutów i zastosowanie ich do kontrolerów, akcji lub parametrów akcji (podobnych do filtrów). W przeciwieństwie do filtrów konwencje są wykonywane tylko wtedy, gdy aplikacja jest uruchamiana, a nie jako część każdego żądania.

Uwaga

Aby uzyskać informacje na Razor temat konwencji dostawcy tras stron i modelu aplikacji, zobacz Razor Strony route and app conventions in ASP.NET Core (Konwencje tras stron i aplikacji w programie ASP.NET Core).

Modyfikowanie ApplicationModel

Następująca konwencja służy do dodawania właściwości do modelu aplikacji:

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

Konwencje modelu aplikacji są stosowane jako opcje po dodaniu wzorca MVC w programie Startup.ConfigureServices:

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

Właściwości są dostępne z kolekcji ActionDescriptor.Properties w ramach akcji kontrolera:

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

Modyfikowanie ControllerModel opisu

Model kontrolera może również zawierać właściwości niestandardowe. Właściwości niestandardowe zastępują istniejące właściwości o tej samej nazwie określonej w modelu aplikacji. Następujący atrybut konwencji dodaje opis na poziomie kontrolera:

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

Ta konwencja jest stosowana jako atrybut na kontrolerze:

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

Modyfikowanie ActionModel opisu

Oddzielna konwencja atrybutów może być stosowana do poszczególnych akcji, przesłaniając zachowanie już stosowane na poziomie aplikacji lub kontrolera:

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

Zastosowanie tej akcji do akcji w kontrolerze pokazuje, jak zastępuje konwencję na poziomie kontrolera:

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

Modyfikowanie ParameterModel

Poniższa konwencja może być stosowana do parametrów akcji w celu zmodyfikowania ich BindingInfo. Poniższa konwencja wymaga, aby parametr był parametrem trasy. Inne potencjalne źródła powiązań, takie jak wartości ciągu zapytania, są ignorowane:

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

Atrybut można zastosować do dowolnego parametru akcji:

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

Aby zastosować konwencję do wszystkich parametrów akcji, dodaj element MustBeInRouteParameterModelConvention do MvcOptions w pliku Startup.ConfigureServices:

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

Modyfikowanie ActionModel nazwy

Poniższa konwencja modyfikuje ActionModel element , aby zaktualizować nazwę akcji, do której została zastosowana. Nowa nazwa jest podawana jako parametr atrybutu. Ta nowa nazwa jest używana przez routing, więc wpływa na trasę używaną do osiągnięcia tej metody akcji:

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

Ten atrybut jest stosowany do metody akcji w pliku HomeController:

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

Mimo że nazwa metody to SomeName, atrybut zastępuje konwencję MVC przy użyciu nazwy metody i zastępuje nazwę akcji .MyCoolAction W związku z tym trasa używana do osiągnięcia tej akcji to /Home/MyCoolAction.

Uwaga

Ten przykład w tej sekcji jest zasadniczo taki sam jak w przypadku korzystania z wbudowanego elementu ActionNameAttribute.

Niestandardowa konwencja routingu

Użyj elementu , IApplicationModelConvention aby dostosować sposób działania routingu. Na przykład następująca konwencja obejmuje przestrzenie nazw kontrolerów w swoich trasach, zastępując . element w przestrzeni nazw / ciągiem w trasie:

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

Konwencja jest dodawana jako opcja w pliku Startup.ConfigureServices:

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

Napiwek

Dodaj konwencje do oprogramowania pośredniczącego przy użyciu MvcOptions następującego podejścia. Symbol {CONVENTION} zastępczy jest konwencją dodawania:

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

Poniższy przykład stosuje konwencję do tras, które nie korzystają z routingu atrybutów, gdzie kontroler ma Namespace swoją nazwę:

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

Użycie modelu aplikacji w programie WebApiCompatShim

ASP.NET Core MVC używa innego zestawu konwencji z interfejsu API sieci Web ASP.NET 2. Korzystając z konwencji niestandardowych, można zmodyfikować zachowanie aplikacji ASP.NET Core MVC w taki sposób, aby była spójna z aplikacją internetowego interfejsu API. Firma Microsoft dostarcza WebApiCompatShim pakiet NuGet specjalnie do tego celu.

Uwaga

Aby uzyskać więcej informacji na temat migracji z internetowego interfejsu API ASP.NET, zobacz Migrowanie z internetowego interfejsu API ASP.NET do ASP.NET Core.

Aby użyć podkładki zgodności internetowego interfejsu API:

  • Microsoft.AspNetCore.Mvc.WebApiCompatShim Dodaj pakiet do projektu.
  • Dodaj konwencje do wzorca MVC, wywołując polecenie AddWebApiConventions w pliku Startup.ConfigureServices:
services.AddMvc().AddWebApiConventions();

Konwencje udostępniane przez podkładki są stosowane tylko do części aplikacji, w których zastosowano pewne atrybuty. Następujące cztery atrybuty służą do kontrolowania, które kontrolery powinny mieć swoje konwencje zmodyfikowane przez konwencje podkładki:

Konwencje akcji

UseWebApiActionConventionsAttribute służy do mapowania metody HTTP na akcje na podstawie ich nazwy (na przykład Get mapuje na HttpGet). Dotyczy tylko akcji, które nie używają routingu atrybutów.

Przeciążenie

UseWebApiOverloadingAttribute służy do stosowania WebApiOverloadingApplicationModelConvention konwencji. Ta konwencja dodaje element OverloadActionConstraint do procesu wyboru akcji, który ogranicza akcje kandydata do tych, dla których żądanie spełnia wszystkie parametry inne niż opcjonalne.

Konwencje parametrów

UseWebApiParameterConventionsAttribute służy do stosowania WebApiParameterConventionsApplicationModelConvention konwencji akcji. Ta konwencja określa, że proste typy używane jako parametry akcji są domyślnie powiązane z identyfikatorem URI, podczas gdy typy złożone są powiązane z treścią żądania.

Trasy

UseWebApiRoutesAttribute określa, WebApiApplicationModelConvention czy jest stosowana konwencja kontrolera. Po włączeniu ta konwencja służy do dodawania obsługi obszarów do trasy i wskazuje, że kontroler znajduje się w api obszarze.

Oprócz zestawu konwencji pakiet zgodności zawiera klasę bazową, która zastępuje klasę System.Web.Http.ApiController określoną przez internetowy interfejs API. Dzięki temu kontrolery internetowego interfejsu API napisane dla internetowego interfejsu API i dziedziczą z jej ApiController działania podczas pracy w ASP.NET Core MVC. UseWebApi* Wszystkie wymienione wcześniej atrybuty są stosowane do klasy kontrolera podstawowego. Uwidacznia ApiController właściwości, metody i typy wyników zgodne z tymi, które znajdują się w internetowym interfejsie API.

Używanie ApiExplorer do dokumentowania aplikacji

Model aplikacji uwidacznia ApiExplorerModel właściwość na każdym poziomie, która może służyć do przechodzenia przez strukturę aplikacji. Może to służyć do generowania stron pomocy dla internetowych interfejsów API przy użyciu narzędzi takich jak Swagger. Właściwość ApiExplorer uwidacznia IsVisible właściwość, którą można ustawić, aby określić, które części modelu aplikacji powinny być uwidocznione. Skonfiguruj to ustawienie przy użyciu konwencji:

using Microsoft.AspNetCore.Mvc.ApplicationModels;

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

Korzystając z tej metody (i dodatkowych konwencji, jeśli jest to wymagane), widoczność interfejsu API jest włączona lub wyłączona na dowolnym poziomie w aplikacji.