Share via


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 인터페이스의 구현은 서로 "래핑"하며, 각 구현은 해당 Order 속성에 따라 오름차순으로 OnProvidersExecuting을 호출합니다. OnProvidersExecuted 메서드는 역순으로 호출됩니다. 프레임워크는 여러 공급자를 정의합니다.

First(Order=-1000):

  • DefaultApplicationModelProvider

Then(Order=-990):

  • AuthorizationApplicationModelProvider
  • CorsApplicationModelProvider

참고 항목

Order에 대해 같은 값을 가진 두 공급자를 호출하는 순서는 정의되지 않았으며 사용하지 말아야 합니다.

참고 항목

IApplicationModelProvider는 프레임워크 확장 작성자를 위한 고급 개념입니다. 일반적으로 앱은 규칙을 사용해야 하고 프레임워크는 공급자를 사용해야 합니다. 중요한 차이점은 공급자가 항상 규칙보다 먼저 실행된다는 점입니다.

DefaultApplicationModelProvider는 ASP.NET Core MVC가 사용하는 대부분의 기본 동작을 설정합니다. 이 공급자의 책임은 다음과 같습니다.

  • 컨텍스트에 전역 필터 추가
  • 컨텍스트에 컨트롤러 추가
  • 공용 컨트롤러 메서드를 작업으로 추가
  • 컨텍스트에 작업 메서드 매개 변수 추가
  • 경로 및 기타 특성 적용

일부 기본 제공 동작은 DefaultApplicationModelProvider에 의해 구현됩니다. 이 공급자는 ControllerModel의 구성을 담당하며, 이는 ActionModel, PropertyModelParameterModel 인스턴스를 차례로 참조합니다. DefaultApplicationModelProvider 클래스는 향후 변경될 수 있는 내부 프레임워크 구현 세부 사항입니다.

AuthorizationApplicationModelProviderAuthorizeFilterAllowAnonymousFilter 특성과 관련된 동작의 적용을 담당합니다. 자세한 내용은 ASP.NET Core의 단순 권한 부여를 참조하세요.

CorsApplicationModelProviderIEnableCorsAttributeIDisableCorsAttribute와 관련된 동작을 구현합니다. 자세한 내용은 ASP.NET Core에서 CORS(원본 간 요청) 사용을 참조하세요.

이 섹션에서 설명하는 프레임워크의 내부 공급자에 대한 정보는 .NET API 브라우저를 통해 사용할 수 없습니다. 그러나 ASP.NET Core 참조 원본(dotnet/aspnetcore GitHub 리포지토리)에서 공급자를 검사할 수 있습니다. GitHub 검색을 사용하여 이름별로 공급자를 찾고 분기/태그 전환 드롭다운 목록을 사용하여 원본 버전을 선택합니다.

규칙

애플리케이션 모델은 전체 모델 또는 공급자를 재정의하기 보다는 모델의 동작을 사용자 지정하는 간단한 방법을 제공하는 규칙 추상화를 정의합니다. 앱의 동작을 수정할 경우 이러한 추상화를 사용하는 것이 좋습니다. 규칙은 사용자 지정을 동적으로 적용하는 코드를 작성하는 방법을 제공합니다. 필터는 프레임워크의 동작을 수정하는 방법을 제공하는 반면, 사용자 지정을 통해 전체 앱이 함께 작동하는 방법을 제어할 수 있습니다.

사용할 수 있는 규칙은 다음과 같습니다.

규칙은 MVC 옵션에 추가하거나 특성을 구현하고 컨트롤러, 작업 또는 작업 매개 변수에 적용하여 적용됩니다(필터와 유사). 필터와 달리 규칙은 각 요청의 일부가 아니라 앱을 시작하는 경우에만 실행됩니다.

참고 항목

Razor Pages 경로 및 애플리케이션 모델 공급자 규칙에 대한 자세한 내용은 RazorASP.NET Core 페이지 경로 및 앱 규칙을 참조하세요.

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

Startup.ConfigureServices에 MVC를 추가할 때 애플리케이션 모델 규칙을 옵션으로 적용합니다.

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

모든 작업 매개 변수에 규칙을 적용하려면 Startup.ConfigureServicesMvcOptionsMustBeInRouteParameterModelConvention을 추가합니다.

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 앱의 동작을 웹 API 앱의 동작과 일치하도록 수정할 수 있습니다. Microsoft는 이를 위해 WebApiCompatShim NuGet 패키지를 제공합니다.

참고 항목

ASP.NET Web API 마이그레이션에 대한 자세한 내용은 ASP.NET Web API에서 ASP.NET Core로 마이그레이션을 참조하세요.

Web API 호환성 Shim을 사용하려면 다음을 수행합니다.

  • 프로젝트에 Microsoft.AspNetCore.Mvc.WebApiCompatShim 패키지를 추가합니다.
  • Startup.ConfigureServices에서 AddWebApiConventions를 호출하여 MVC에 규칙을 추가합니다.
services.AddMvc().AddWebApiConventions();

shim에서 제공하는 규칙은 특정 특성이 적용된 앱의 일부에만 적용됩니다. 다음 네 가지 특성이 shim의 규칙에 따라 규칙을 수정해야 하는 컨트롤러를 제어하는 데 사용됩니다.

작업 규칙

UseWebApiActionConventionsAttribute는 작업 이름을 기준으로 HTTP 메서드를 작업에 매핑하는 데 사용됩니다(예: GetHttpGet에 매핑됨). 특성 라우팅을 사용하지 않는 작업에만 적용됩니다.

오버로딩

UseWebApiOverloadingAttributeWebApiOverloadingApplicationModelConvention 규칙을 적용하는 데 사용됩니다. 이 규칙은 작업 선택 프로세스에 OverloadActionConstraint를 추가하는데, 이는 후보 작업을 요청이 모든 비 선택적 매개 변수를 충족시키는 작업으로 제한합니다.

매개 변수 규칙

UseWebApiParameterConventionsAttributeWebApiParameterConventionsApplicationModelConvention 작업 규칙을 적용하는 데 사용됩니다. 이 규칙은 작업 매개 변수로 사용된 단순 형식은 기본적으로 URI에서 바인드되는 반면 복합 형식은 요청 본문에서 바인드되도록 지정합니다.

경로

UseWebApiRoutesAttributeWebApiApplicationModelConvention 컨트롤러 규칙을 적용할지 여부를 제어합니다. 이 규칙을 사용하면 경로에 영역에 대한 지원을 추가하고 컨트롤러가 api 영역에 있음을 나타냅니다.

호환성 패키지에는 규칙 집합 외에도 웹 API에서 제공하는 클래스를 대신하는 System.Web.Http.ApiController 기본 클래스가 포함되어 있습니다. 이를 사용하면 웹 API용으로 작성되고 해당 ApiController에서 상속된 웹 API 컨트롤러를 ASP.NET Core MVC에서 실행하더라도 작동시킬 수 있습니다. 앞서 나열된 모든 UseWebApi* 특성은 기본 컨트롤러 클래스에 적용됩니다. ApiController는 웹 API에서 찾을 수 있는 것과 호환되는 속성, 메서드 및 결과 형식을 노출합니다.

ApiExplorer를 사용하여 앱 문서화

응용 프로그램 모델은 앱의 구조를 트래버스하는 데 사용할 수 있는 ApiExplorerModel 속성을 각 수준에서 노출합니다. 이를 Swagger와 같은 도구를 사용하여 웹 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 가시성을 사용하거나 사용하지 않을 수 있습니다.