ASP.NET Core에서 필터링

작성자: Kirk Larkin, Rick Anderson, Tom DykstraSteve Smith

ASP.NET Core의 필터를 사용하면 요청 처리 파이프라인의 특정 단계 전후에 코드를 실행할 수 있습니다.

기본 제공 필터는 다음과 같은 작업을 처리합니다.

  • 권한 부여(사용자가 권한이 없는 리소스에 액세스하는 것을 방지).
  • 응답 캐싱(요청 파이프라인을 단락하여 캐시된 응답 반환).

사용자 지정 필터를 만들어 횡단 관심사를 처리할 수 있습니다. 횡단 관심사의 사례로는 오류 처리, 캐싱, 구성, 권한 부여 및 로깅을 들 수 있습니다. 필터는 코드 중복을 방지합니다. 예를 들어 오류 처리 예외 필터는 오류 처리를 통합할 수 있습니다.

이 문서는 Razor Pages, API 컨트롤러 및 보기를 사용하는 컨트롤러에 적용됩니다. 필터는 작동 시 Razor 구성 요소에 직접적인 영향을 주지 않습니다. 필터는 다음과 같은 경우에만 간접적으로 구성 요소에 영향을 줄 수 있습니다.

  • 구성 요소가 페이지 또는 보기에 포함되어 있는 경우
  • 페이지 또는 컨트롤러 및 보기에서 필터를 사용하는 경우

필터 작동 방법

필터는 ‘필터 파이프라인’이라고도 하는 ‘ASP.NET Core 동작 호출 파이프라인’내에서 실행됩니다. 필터 파이프라인은 ASP.NET Core가 실행할 작업을 선택한 후에 실행됩니다.

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

필터 유형

각 필터 형식은 필터 파이프라인의 다른 단계에서 실행됩니다.

  • 권한 부여 필터:

    • 먼저 실행합니다.
    • 사용자에게 요청 권한이 있는지 여부를 확인합니다.
    • 요청 권한이 없는 경우 파이프라인을 단락합니다.
  • 리소스 필터:

    • 권한 부여 후 실행됩니다.
    • OnResourceExecuting는 나머지 필터 파이프라인보다 먼저 코드를 실행합니다. 예를 들어 OnResourceExecuting는 모델 바인딩 전에 코드를 실행합니다.
    • OnResourceExecuted는 파이프라인의 나머지 부분이 완료된 후에 코드를 실행합니다.
  • 작업 필터:

    • 작업 메서드가 호출되기 전후에 즉시 실행합니다.
    • 작업에 전달된 인수를 변경할 수 있습니다.
    • 작업에서 반환된 결과를 변경할 수 있습니다.
    • Razor Pages에서 지원되지 않습니다.
  • 엔드포인트 필터:

    • 작업 메서드가 호출되기 전후에 즉시 실행합니다.
    • 작업에 전달된 인수를 변경할 수 있습니다.
    • 작업에서 반환된 결과를 변경할 수 있습니다.
    • Razor Pages에서 지원되지 않습니다.
    • 작업 및 경로 처리기 기반 엔드포인트 모두에서 호출할 수 있습니다.
  • 예외 필터는 응답 본문이 쓰여지기 전에 발생한 처리되지 않은 예외에 대한 전역 정책을 적용합니다.

  • 결과 필터는:

    • 작업 결과 실행 전후에 즉시 실행됩니다.
    • 작업 메서드가 성공적으로 실행될 때만 실행됩니다.
    • 보기 또는 포맷터 실행을 둘러싸야 하는 논리에 유용합니다.

다음 다이어그램은 필터 형식이 필터 파이프라인에서 상호 작용하는 방법을 보여줍니다.

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Pages는 Razor 페이지 처리기 전후에 실행되는 Razor 페이지 필터도 지원합니다.

구현

필터는 서로 다른 인터페이스 정의를 통해 동기 및 비동기 구현을 모두 지원합니다.

동기 필터는 해당 파이프라인 단계 전후에 실행됩니다. 예를 들어 OnActionExecuting는 작업 메서드가 호출되기 전에 호출됩니다. OnActionExecuted는 작업 메서드가 반환된 후 호출됩니다.

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

비동기 필터는 On-Stage-ExecutionAsync 메서드를 정의합니다. 예: OnActionExecutionAsync

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

이전 코드에서 SampleAsyncActionFilter는 작업 메서드를 실행하는 ActionExecutionDelegate, next를 가지고 있습니다.

여러 필터 단계

단일 클래스에서 여러 필터 단계에 대한 인터페이스를 구현할 수 있습니다. 예를 들어 ActionFilterAttribute를 구현합니다.

필터 인터페이스의 동기 또는 비동기 버전을 모두 구현하지 말고 그 중 한 가지만 구현하세요. 런타임은 먼저 필터가 비동기 인터페이스를 구현하는지를 확인하고 그렇다면 이를 호출합니다. 그렇지 않으면 동기 인터페이스의 메서드를 호출합니다. 비동기 및 동기 인터페이스가 모두 하나의 클래스에 구현된 경우에는 비동기 메서드만 호출됩니다. ActionFilterAttribute 같은 추상 클래스를 사용하는 경우 각 필터 형식에 대한 동기 메서드 또는 비동기 메서드만 재정의합니다.

기본 제공 필터 특성

ASP.NET Core에는 서브클래싱 및 사용자 지정할 수 있는 기본 제공 특성 기반 필터가 포함되어 있습니다. 예를 들어 다음 결과 필터는 응답에 헤더를 추가합니다.

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

특성을 사용하면 이전 예제와 같이 필터에서 인수를 받을 수 있습니다. 컨트롤러나 작업 메서드에 ResponseHeaderAttribute를 적용하고 HTTP 헤더의 이름 및 값을 지정합니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

브라우저 개발자 도구와 같은 도구를 사용하여 헤더를 검사합니다. 응답 헤더filter-header: Filter Value이 표시됩니다.

다음 코드는 컨트롤러와 작업 모두에 ResponseHeaderAttribute를 적용합니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Multiple 작업의 응답에는 다음 헤더가 포함됩니다.

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

여러 필터 인터페이스에는 사용자 지정 구현에 대한 기본 클래스로 사용할 수 있는 해당 특성이 있습니다.

필터 특성:

Razor Page 처리기 메서드에는 필터를 적용할 수 없습니다. Razor Page 모델 또는 전역적으로 적용할 수 있습니다.

필터 범위 및 실행 순서

세 가지 범위 중 하나에서 필터를 파이프라인에 추가할 수 있습니다.

  • 컨트롤러 또는 Razor Page에서 특성 사용.
  • 컨트롤러 작업에서 특성 사용. 필터 특성은 Razor Pages 처리기 메서드에 적용할 수 없습니다.
  • 다음 코드와 같이 모든 컨트롤러, 작업 및 Razor Pages에 전역으로 사용:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

기본 실행 순서

파이프라인의 특정 단계에 여러 개의 필터가 있는 경우 범위가 기본 필터 실행 순서를 결정합니다. 전역 필터는 메서드 필터를 둘러싼 클래스 필터를 둘러쌉니다.

필터 중첩의 결과로 필터의 after 코드는 before 코드의 역순으로 실행됩니다. 필터의 순서는 다음과 같습니다.

  • 전역 필터의 before 코드.
    • 컨트롤러 필터의 before 코드.
      • 작업 메서드 필터의 before 코드.
      • 작업 메서드 필터의 after 코드.
    • 컨트롤러 필터의 after 코드.
  • 전역 필터의 after 코드.

다음 예제는 동기 작업 필터에 대해 필터 메서드가 실행되는 순서를 보여 줍니다.

Sequence 필터 범위 필터 메서드
1 전역 OnActionExecuting
2 Controller OnActionExecuting
3 작업 OnActionExecuting
4 작업 OnActionExecuted
5 Controller OnActionExecuted
6 전역 OnActionExecuted

컨트롤러 수준 필터

Controller에서 상속되는 모든 컨트롤러에는 OnActionExecuting, OnActionExecutionAsyncOnActionExecuted 메서드가 포함됩니다. 이러한 메서드는 지정된 작업을 위해 실행되는 필터를 래핑합니다.

  • OnActionExecuting는 작업 필터 이전에 실행됩니다.
  • OnActionExecuted는 작업의 모든 작업 필터 후에 실행됩니다.
  • OnActionExecutionAsync는 작업 필터 이전에 실행됩니다. next를 호출한 후의 코드는 작업의 필터 후에 실행됩니다.

다음 ControllerFiltersController 클래스:

  • 컨트롤러에 SampleActionFilterAttribute ([SampleActionFilter])를 적용합니다.
  • OnActionExecutingOnActionExecuted를 재정의합니다.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

https://localhost:<port>/ControllerFilters로 이동하면 다음 코드가 실행됩니다.

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

컨트롤러 수준 필터는 Order 속성을 int.MinValue로 설정합니다. 컨트롤러 수준 필터는 메서드에 적용된 후에 실행되도록 설정할 수 없습니다. 순서는 다음 섹션에 설명되어 있습니다.

Razor Pages의 경우 필터 메서드를 재정의하여 Razor Page 필터 구현을 참조하세요.

기본값 순서 재정의

IOrderedFilter를 구현하여 실행의 기본 순서를 재정의할 수 있습니다. IOrderedFilter은 실행 순서를 결정하는 데 범위보다 우선 순위가 높은 Order 속성을 노출합니다. 낮은 Order 값을 가진 필터는:

  • 더 높은 Order 값을 가진 필터 이전에 before 코드를 실행합니다.
  • 더 높은 Order 값을 가진 필터 이후에 after 코드를 실행합니다.

컨트롤러 수준 필터 예제에서 GlobalSampleActionFilter는 전역 범위를 가지므로 컨트롤러 범위가 있는 SampleActionFilterAttribute 전에 실행됩니다. SampleActionFilterAttribute를 먼저 실행하려면 순서를 int.MinValue로 설정합니다.

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

전역 필터 GlobalSampleActionFilter를 먼저 실행하려면 이 필터의 Orderint.MinValue로 설정합니다.

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

취소 및 단락

필터 메서드에 제공되는 ResourceExecutingContext 매개 변수의 Result 속성을 설정하여 필터 파이프라인을 단락시킬 수 있습니다. 예를 들어 다음 리소스 필터는 파이프라인의 나머지 부분이 실행되지 못하도록 합니다.

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

다음 코드에서 [ShortCircuitingResourceFilter][ResponseHeader] 필터는 Index 작업 메서드를 대상으로 지정합니다. ShortCircuitingResourceFilterAttribute 필터:

  • 리소스 필터이고 ResponseHeaderAttribute는 작업 필터이기 때문에 먼저 실행됩니다.
  • 나머지 파이프라인을 단락시킵니다.

따라서 ResponseHeaderAttribute 필터는 Index 작업에 대해 절대 실행되지 않습니다. 이 동작은 ShortCircuitingResourceFilterAttribute가 먼저 실행되기 때문에 작업 메서드 수준에서 두 필터를 적용하더라도 동일합니다. 필터 형식으로 인해 ShortCircuitingResourceFilterAttribute가 먼저 실행됩니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

종속성 주입

형식 또는 인스턴스별로 필터를 추가할 수 있습니다. 인스턴스가 추가되면 모든 요청에 해당 인스턴스가 사용됩니다. 형식이 추가되면 해당 필터는 형식으로 활성화됩니다. 형식으로 활성화된 필터는 다음을 의미합니다.

  • 각 요청에 대해 인스턴스가 만들어집니다.
  • 모든 생성자 종속성이 DI(종속성 주입)를 통해서 채워집니다.

특성으로 구현되고 컨트롤러 클래스 또는 작업 메서드에 직접 추가되는 필터에는 DI(종속성 주입)에서 제공하는 생성자 종속성이 없을 것입니다. 특성에 해당 생성자 매개 변수가 적용된 위치에 제공되어야 하므로 DI에서 생성자 종속성을 제공할 수 없습니다.

다음 필터는 DI에서 제공하는 생성자 종속성을 지원합니다.

이전 필터는 컨트롤러 또는 작업에 적용할 수 있습니다.

로거는 DI를 통해서 사용할 수 있습니다. 그러나 로깅 용도로만 필터를 만들거나 사용하지는 마세요. 기본 제공 프레임워크 로깅은 일반적으로 로깅에 필요한 기능을 제공합니다. 필터에 추가된 로깅은:

  • 비즈니스 도메인 문제 또는 필터 고유의 동작에 중점을 두어야 합니다.
  • 작업 또는 다른 프레임워크 이벤트를 로깅해서는 안 됩니다. 기본 제공 필터는 작업 및 프레임워크 이벤트를 미리 로그합니다.

ServiceFilterAttribute

서비스 필터 구현 형식은 Program.cs에 등록됩니다. ServiceFilterAttribute는 DI에서 필터의 인스턴스를 검색합니다.

다음 코드는 DI를 사용하는 LoggingResponseHeaderFilterService 클래스를 보여줍니다.

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

다음 코드에서 LoggingResponseHeaderFilterService는 DI 컨테이너에 추가됩니다.

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

다음 코드에서 ServiceFilter 특성은 DI에서 LoggingResponseHeaderFilterService 필터의 인스턴스를 검색합니다.

[ServiceFilter<LoggingResponseHeaderFilterService>]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

ServiceFilterAttribute를 사용하는 경우 ServiceFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 외부에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 다음을 보장하지 않습니다.
    • 필터의 단일 인스턴스 생성.
    • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.
  • 싱글톤 이외의 수명이 지정된 서비스에 의존하는 필터와 함께 사용하지 마세요.

ServiceFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 DI에서 지정된 형식을 로드합니다.

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute와 비슷하지만 해당 형식은 DI 컨테이너에서 직접 해결되지 않습니다. Microsoft.Extensions.DependencyInjection.ObjectFactory를 사용하여 형식을 인스턴스화합니다.

TypeFilterAttribute 형식은 DI 컨테이너에서 직접 해결되지 않기 때문입니다.

  • TypeFilterAttribute을 사용하여 참조되는 형식은 DI 컨테이너를 사용하여 등록할 필요가 없습니다. DI 컨테이너에서 충족하는 종속성을 갖고 있습니다.
  • TypeFilterAttribute는 형식에 대한 생성자 인수를 필요에 따라 받을 수 있습니다.

TypeFilterAttribute를 사용하는 경우 TypeFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 밖에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 단일 필터 인스턴스가 생성되도록 보장하지 않습니다.

  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

다음 예제는 TypeFilterAttribute를 사용하여 형식에 인수를 전달하는 방법을 보여줍니다.

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

권한 부여 필터

권한 부여 필터는:

  • 필터 파이프라인에서 첫 번째로 실행되는 필터입니다.
  • 작업 메서드에 대한 액세스를 제어합니다.
  • before 메서드는 있지만 after 메서드는 없습니다.

사용자 지정 권한 부여 필터는 사용자 지정 권한 부여 프레임워크를 필요로 합니다. 사용자 지정 필터를 작성하는 대신 권한 부여 정책을 구성하거나 사용자 지정 권한 부여 정책을 작성하는 것이 좋습니다. 기본 제공 권한 부여 필터는:

  • 권한 부여 시스템을 호출합니다.
  • 요청을 승인하지 않습니다.

권한 부여 필터 내에서 예외를 던지지 마세요.

  • 해당 예외는 처리되지 않습니다.
  • 예외 필터가 해당 예외를 처리하지 않습니다.

권한 부여 필터에서 예외가 발생할 경우 챌린지 발행을 고려하세요.

권한 부여에 대해 자세히 알아봅니다.

리소스 필터

리소스 필터는:

리소스 필터는 파이프라인의 대부분을 단락시켜야 하는 경우에 유용합니다. 예를 들어 캐싱 필터는 캐시 적중 시 파이프라인의 나머지 부분을 막을 수 있습니다.

리소스 필터 예제:

작업 필터

작업 필터는 Razor Pages에 적용되지 않습니다. Razor Pages는 IPageFilterIAsyncPageFilter를 지원합니다. 자세한 내용은 Razor Pages에 대한 필터 메서드를 참조하세요.

작업 필터는:

다음 코드는 예제 작업 필터를 보여 줍니다.

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

ActionExecutingContext는 다음과 같은 속성을 제공합니다.

  • ActionArguments - 작업 메서드에 대한 입력을 읽을 수 있습니다.
  • Controller - 컨트롤러 인스턴스를 조작할 수 있습니다.
  • Result - Result 설정은 작업 메서드 및 후속 작업 필터의 실행을 단락시킵니다.

작업 메서드에서 예외를 던지면:

  • 후속 필터의 실행을 막습니다.
  • Result 설정과 달리 성공적인 결과 대신 실패로 처리됩니다.

ActionExecutedContextControllerResult에 더하여 다음과 같은 속성을 제공합니다.

  • Canceled - 작업 실행이 다른 필터에 의해 단락된 경우 true입니다.
  • Exception - 작업 또는 이전에 실행된 작업 필터에서 예외가 던져진 경우 null이 아닙니다. 이 속성을 null로 설정:
    • 예외를 효과적으로 처리합니다.
    • Result는 작업 메서드에서 반환되는 것처럼 실행됩니다.

IAsyncActionFilter의 경우 ActionExecutionDelegate에 대한 호출은:

  • 후속 작업 필터 및 작업 메서드를 실행합니다.
  • ActionExecutedContext을(를) 반환합니다.

단락시키려면 Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result을 결과 인스턴스에 할당하고 next(ActionExecutionDelegate)를 호출하지 않아야 합니다.

프레임워크는 서브클래싱 할 수 있는 추상 ActionFilterAttribute를 제공합니다.

OnActionExecuting 작업 필터는 다음에 사용할 수 있습니다.

  • 모델 상태의 유효성을 검사합니다.
  • 상태가 유효하지 않은 경우 오류를 반환합니다.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

참고 항목

[ApiController] 특성으로 주석이 추가된 컨트롤러는 모델 상태의 유효성을 자동으로 검사하고 400 응답을 반환합니다. 자세한 정보는 자동 HTTP 400 응답을 참조하세요.

OnActionExecuted 메서드는 작업 메서드 이후에 실행됩니다.

  • 그리고 Result 속성을 통해 작업 결과를 확인하고 조작할 수 있습니다.
  • Canceled는 작업 실행이 다른 필터에 의해 단락된 경우 true로 설정됩니다.
  • Exception은 작업 또는 후속 작업 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. null로 설정 Exception :
    • 예외를 효과적으로 처리합니다.
    • ActionExecutedContext.Result은 작업 메서드에서 정상적으로 반환되는 것처럼 실행됩니다.

예외 필터

예외 필터:

다음 샘플 예외 필터는 앱이 개발 중일 때 발생하는 예외에 대한 세부 정보를 표시합니다.

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

다음 코드에서는 예외 필터를 테스트합니다.

[TypeFilter<SampleExceptionFilter>]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

예외 필터:

  • before 및 after 이벤트가 없습니다.
  • OnException 또는 OnExceptionAsync를 구현합니다.
  • Razor Page 또는 컨트롤러 생성,모델 바인딩, 작업 필터 또는 작업 메서드에서 발생하는 처리되지 않은 예외를 처리합니다.
  • 리소스 필터, 결과 필터 또는 MVC 결과 실행에서 발생하는 예외를 잡지 않습니다.

예외를 처리하려면 ExceptionHandled 속성을 true로 설정하거나 Result 속성을 할당합니다. 그러면 예외가 전파되지 않습니다. 예외 필터는 예외를 “성공”으로 변환할 수 없습니다. 이는 작업 필터에서만 가능합니다.

예외 필터:

  • 작업 내에서 발생하는 예외를 잡는 데 좋습니다.
  • 오류 처리 미들웨어만큼 유연하지 않습니다.

예외 처리의 경우 미들웨어를 선호합니다. 어떤 작업 메서드가 호출되는지에 따라 오류 처리 방식이 ‘다른’ 경우에만 예외 필터를 사용합니다. 예를 들어 앱에는 API 엔드포인트 및 보기/HTML 모두에 대한 작업 메서드가 있을 수 있습니다. API 엔드포인트는 JSON으로 오류 정보를 반환할 수 있습니다. 반면 보기 기반 작업은 HTML로 오류 페이지를 반환할 수 있습니다.

결과 필터

결과 필터는:

IResultFilter 및 IAsyncResultFilter

다음 코드는 샘플 작업 필터를 보여 줍니다.

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

실행되는 결과의 종류는 작업에 따라 다릅니다. 보기를 반환하는 작업에는 실행 중인 ViewResult의 일부로 모든 Razor 프로세스가 포함됩니다. API 메서드는 실행 결과의 일부로 일부 serialization을 수행할 수 있습니다. 작업 결과에 대해 자세히 알아보세요.

결과 필터는 작업 또는 작업 필터가 작업 결과를 생성하는 경우에만 실행됩니다. 다음 경우에는 결과 필터가 실행되지 않습니다.

  • 권한 부여 필터 또는 리소스 필터가 파이프라인을 단락시킬 경우.
  • 예외 필터는 작업 결과를 생성하여 예외를 처리합니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 메서드는 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Canceltrue로 설정하여 작업 결과 및 후속 결과 필터를 단락시킬 수 있습니다. 단락시킬 경우 빈 응답을 생성하지 않도록 응답 개체에 작성하세요. IResultFilter.OnResultExecuting에서 예외 throw:

  • 작업 결과 및 후속 필터의 실행을 막습니다.
  • 성공적인 결과 대신 실패로 처리됩니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 메서드가 실행될 때에는 응답이 이미 클라이언트에 전송되었을 수 있습니다. 이미 클라이언트에 전송된 응답은 변경할 수 없습니다.

ResultExecutedContext.Canceled는 작업 결과 실행이 다른 필터에 의해 단락(short-circuit) 처리된 경우 true로 설정됩니다.

ResultExecutedContext.Exception은 작업 결과 또는 후속 결과 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. Exception을 효과적으로 null로 설정하면 예외를 '처리'하고 예외가 파이프라인의 뒷부분에서 다시 throw되지 않습니다. 결과 필터에서 예외를 처리하는 경우 데이터를 응답에 쓸 수 있는 신뢰할 수 있는 방법이 없습니다. 작업 결과가 예외를 던질 때 헤더가 클라이언트에 플러시된 경우 오류 코드를 전송하기 위한 신뢰할 수 있는 메커니즘이 없습니다.

IAsyncResultFilter의 경우 ResultExecutionDelegateawait next 호출은 후속 결과 필터 및 작업 결과를 실행합니다. 단락(short-circuit) 처리하려면 ResultExecutingContext.Canceltrue로 설정하고 ResultExecutionDelegate를 호출하지 않습니다.

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

프레임워크는 서브클래싱 할 수 있는 추상 ResultFilterAttribute를 제공합니다. 이전에 표시된 ResponseHeaderAttribute 클래스는 결과 필터 특성의 예입니다.

IAlwaysRunResultFilter 및 IAsyncAlwaysRunResultFilter

IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter 인터페이스는 모든 작업 결과에 대해 실행되는 IResultFilter 구현을 선언합니다. 여기에는 다음에 의해 생성되는 작업 결과가 포함됩니다.

  • 단락하는 권한 부여 필터 및 리소스 필터
  • 예외 필터

예를 들어 다음 필터는 항상 실행되어 콘텐츠 협상이 실패할 경우 작업 결과(ObjectResult)를 422 Unprocessable Entity 상태 코드로 설정합니다.

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactoryIFilterMetadata를 구현합니다. 따라서 필터 파이프라인 어디에서나 IFilterFactory 인스턴스를 IFilterMetadata 인스턴스로 사용할 수 있습니다. 런타임이 필터를 호출하려고 준비할 때 IFilterFactory로 캐스팅을 시도합니다. 해당 캐스팅에 성공하면 CreateInstance 메서드를 호출하여 호출되는 IFilterMetadata 인스턴스를 만듭니다. 앱이 시작될 때 정확한 필터 파이프라인을 명시적으로 설정할 필요가 없으므로 유연한 디자인을 제공합니다.

IFilterFactory.IsReusable:

  • 팩터리에서 만든 필터 인스턴스가 생성된 요청 범위 외부에서 재사용될 수 있다는 힌트입니다.
  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

ASP.NET Core 런타임은 다음을 보장하지 않습니다.

  • 필터의 단일 인스턴스 생성.
  • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.

Warning

필터의 원본이 명확하고, 필터가 상태 비저장이며, 필터가 여러 HTTP 요청에서 안전하게 사용할 수 있는 경우에만 true를 반환하도록 IFilterFactory.IsReusable을 구성합니다. 예를 들어 IFilterFactory.IsReusabletrue를 반환하는 경우 범위가 지정되거나 일시적으로 등록된 DI에서 필터를 반환하지 마세요.

필터를 만드는 다른 방법으로 사용자 지정 특성 구현을 사용하여 IFilterFactory를 구현할 수 있습니다.

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

필터는 다음 코드에서 적용됩니다.

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

특성에서 구현된 IFilterFactory

IFilterFactory를 구현하는 필터는 다음과 같은 필터에 유용합니다.

  • 매개 변수 전달을 필요로 하지 않는 필터.
  • DI에 의해 채워져야 할 생성자 종속성이 있는 필터.

TypeFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 서비스 컨테이너(DI)에서 지정된 형식을 로드합니다.

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

다음 코드에서는 필터를 적용하는 세 가지 방법을 보여 줍니다.

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter<SampleActionTypeFilterAttribute>]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

이전 코드에서는 필터를 적용하는 첫 번째 방법을 사용하는 것이 좋습니다.

필터 파이프라인에서 미들웨어 사용

리소스 필터는 파이프라인의 뒷부분에 제공되는 모든 실행을 둘러싼다는 점에서 미들웨어처럼 작동합니다. 하지만 필터는 런타임의 일부라는 점에서 미들웨어와 다릅니다. 즉, 컨텍스트 및 구문에 액세스할 수 있습니다.

미들웨어를 필터로 사용하려면 필터 파이프라인에 삽입할 미들웨어를 지정하는 Configure 메서드를 포함한 형식을 만듭니다. 다음 예제에서는 미들웨어를 사용하여 응답 헤더를 설정합니다.

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

MiddlewareFilterAttribute를 사용하여 미들웨어를 실행합니다.

[MiddlewareFilter<FilterMiddlewarePipeline>]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

미들웨어 필터는 모델 바인딩 이전 및 나머지 파이프라인 이후에 리소스 필터와 동일한 필터 파이프라인 단계에서 실행됩니다.

스레드로부터의 안전성

Type 대신 필터의 인스턴스Add에 전달하는 경우 필터는 싱글톤이며 스레드로부터 안전하지 않습니다.

추가 리소스

작성자: Kirk Larkin, Rick Anderson, Tom DykstraSteve Smith

ASP.NET Core의 필터를 사용하면 요청 처리 파이프라인의 특정 단계 전후에 코드를 실행할 수 있습니다.

기본 제공 필터는 다음과 같은 작업을 처리합니다.

  • 권한 부여(사용자가 권한이 없는 리소스에 액세스하는 것을 방지).
  • 응답 캐싱(요청 파이프라인을 단락하여 캐시된 응답 반환).

사용자 지정 필터를 만들어 횡단 관심사를 처리할 수 있습니다. 횡단 관심사의 사례로는 오류 처리, 캐싱, 구성, 권한 부여 및 로깅을 들 수 있습니다. 필터는 코드 중복을 방지합니다. 예를 들어 오류 처리 예외 필터는 오류 처리를 통합할 수 있습니다.

이 문서는 Razor Pages, API 컨트롤러 및 보기를 사용하는 컨트롤러에 적용됩니다. 필터는 작동 시 Razor 구성 요소에 직접적인 영향을 주지 않습니다. 필터는 다음과 같은 경우에만 간접적으로 구성 요소에 영향을 줄 수 있습니다.

  • 구성 요소가 페이지 또는 보기에 포함되어 있는 경우
  • 페이지 또는 컨트롤러 및 보기에서 필터를 사용하는 경우

필터 작동 방법

필터는 ‘필터 파이프라인’이라고도 하는 ‘ASP.NET Core 동작 호출 파이프라인’내에서 실행됩니다. 필터 파이프라인은 ASP.NET Core가 실행할 작업을 선택한 후에 실행됩니다.

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

필터 유형

각 필터 형식은 필터 파이프라인의 다른 단계에서 실행됩니다.

  • 권한 부여 필터:

    • 먼저 실행합니다.
    • 사용자에게 요청 권한이 있는지 여부를 확인합니다.
    • 요청 권한이 없는 경우 파이프라인을 단락합니다.
  • 리소스 필터:

    • 권한 부여 후 실행됩니다.
    • OnResourceExecuting는 나머지 필터 파이프라인보다 먼저 코드를 실행합니다. 예를 들어 OnResourceExecuting는 모델 바인딩 전에 코드를 실행합니다.
    • OnResourceExecuted는 파이프라인의 나머지 부분이 완료된 후에 코드를 실행합니다.
  • 작업 필터:

    • 작업 메서드가 호출되기 전후에 즉시 실행합니다.
    • 작업에 전달된 인수를 변경할 수 있습니다.
    • 작업에서 반환된 결과를 변경할 수 있습니다.
    • Razor Pages에서 지원되지 않습니다.
  • 엔드포인트 필터:

    • 작업 메서드가 호출되기 전후에 즉시 실행합니다.
    • 작업에 전달된 인수를 변경할 수 있습니다.
    • 작업에서 반환된 결과를 변경할 수 있습니다.
    • Razor Pages에서 지원되지 않습니다.
    • 작업 및 경로 처리기 기반 엔드포인트 모두에서 호출할 수 있습니다.
  • 예외 필터는 응답 본문이 쓰여지기 전에 발생한 처리되지 않은 예외에 대한 전역 정책을 적용합니다.

  • 결과 필터는:

    • 작업 결과 실행 전후에 즉시 실행됩니다.
    • 작업 메서드가 성공적으로 실행될 때만 실행됩니다.
    • 보기 또는 포맷터 실행을 둘러싸야 하는 논리에 유용합니다.

다음 다이어그램은 필터 형식이 필터 파이프라인에서 상호 작용하는 방법을 보여줍니다.

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

Razor Pages는 Razor 페이지 처리기 전후에 실행되는 Razor 페이지 필터도 지원합니다.

구현

필터는 서로 다른 인터페이스 정의를 통해 동기 및 비동기 구현을 모두 지원합니다.

동기 필터는 해당 파이프라인 단계 전후에 실행됩니다. 예를 들어 OnActionExecuting는 작업 메서드가 호출되기 전에 호출됩니다. OnActionExecuted는 작업 메서드가 반환된 후 호출됩니다.

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

비동기 필터는 On-Stage-ExecutionAsync 메서드를 정의합니다. 예: OnActionExecutionAsync

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // Do something before the action executes.
        await next();
        // Do something after the action executes.
    }
}

이전 코드에서 SampleAsyncActionFilter는 작업 메서드를 실행하는 ActionExecutionDelegate, next를 가지고 있습니다.

여러 필터 단계

단일 클래스에서 여러 필터 단계에 대한 인터페이스를 구현할 수 있습니다. 예를 들어 ActionFilterAttribute를 구현합니다.

필터 인터페이스의 동기 또는 비동기 버전을 모두 구현하지 말고 그 중 한 가지만 구현하세요. 런타임은 먼저 필터가 비동기 인터페이스를 구현하는지를 확인하고 그렇다면 이를 호출합니다. 그렇지 않으면 동기 인터페이스의 메서드를 호출합니다. 비동기 및 동기 인터페이스가 모두 하나의 클래스에 구현된 경우에는 비동기 메서드만 호출됩니다. ActionFilterAttribute 같은 추상 클래스를 사용하는 경우 각 필터 형식에 대한 동기 메서드 또는 비동기 메서드만 재정의합니다.

기본 제공 필터 특성

ASP.NET Core에는 서브클래싱 및 사용자 지정할 수 있는 기본 제공 특성 기반 필터가 포함되어 있습니다. 예를 들어 다음 결과 필터는 응답에 헤더를 추가합니다.

public class ResponseHeaderAttribute : ActionFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public ResponseHeaderAttribute(string name, string value) =>
        (_name, _value) = (name, value);

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_name, _value);

        base.OnResultExecuting(context);
    }
}

특성을 사용하면 이전 예제와 같이 필터에서 인수를 받을 수 있습니다. 컨트롤러나 작업 메서드에 ResponseHeaderAttribute를 적용하고 HTTP 헤더의 이름 및 값을 지정합니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

브라우저 개발자 도구와 같은 도구를 사용하여 헤더를 검사합니다. 응답 헤더filter-header: Filter Value이 표시됩니다.

다음 코드는 컨트롤러와 작업 모두에 ResponseHeaderAttribute를 적용합니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ResponseHeaderController : ControllerBase
{
    public IActionResult Index() =>
        Content("Examine the response headers using the F12 developer tools.");

    // ...

    [ResponseHeader("Another-Filter-Header", "Another Filter Value")]
    public IActionResult Multiple() =>
        Content("Examine the response headers using the F12 developer tools.");
}

Multiple 작업의 응답에는 다음 헤더가 포함됩니다.

  • filter-header: Filter Value
  • another-filter-header: Another Filter Value

여러 필터 인터페이스에는 사용자 지정 구현에 대한 기본 클래스로 사용할 수 있는 해당 특성이 있습니다.

필터 특성:

Razor Page 처리기 메서드에는 필터를 적용할 수 없습니다. Razor Page 모델 또는 전역적으로 적용할 수 있습니다.

필터 범위 및 실행 순서

세 가지 범위 중 하나에서 필터를 파이프라인에 추가할 수 있습니다.

  • 컨트롤러 또는 Razor Page에서 특성 사용.
  • 컨트롤러 작업에서 특성 사용. 필터 특성은 Razor Pages 처리기 메서드에 적용할 수 없습니다.
  • 다음 코드와 같이 모든 컨트롤러, 작업 및 Razor Pages에 전역으로 사용:
    var builder = WebApplication.CreateBuilder(args);
    
    // Add services to the container.
    builder.Services.AddControllersWithViews(options =>
    {
        options.Filters.Add<GlobalSampleActionFilter>();
    });
    

기본 실행 순서

파이프라인의 특정 단계에 여러 개의 필터가 있는 경우 범위가 기본 필터 실행 순서를 결정합니다. 전역 필터는 메서드 필터를 둘러싼 클래스 필터를 둘러쌉니다.

필터 중첩의 결과로 필터의 after 코드는 before 코드의 역순으로 실행됩니다. 필터의 순서는 다음과 같습니다.

  • 전역 필터의 before 코드.
    • 컨트롤러 필터의 before 코드.
      • 작업 메서드 필터의 before 코드.
      • 작업 메서드 필터의 after 코드.
    • 컨트롤러 필터의 after 코드.
  • 전역 필터의 after 코드.

다음 예제는 동기 작업 필터에 대해 필터 메서드가 실행되는 순서를 보여 줍니다.

Sequence 필터 범위 필터 메서드
1 전역 OnActionExecuting
2 Controller OnActionExecuting
3 작업 OnActionExecuting
4 작업 OnActionExecuted
5 Controller OnActionExecuted
6 전역 OnActionExecuted

컨트롤러 수준 필터

Controller에서 상속되는 모든 컨트롤러에는 OnActionExecuting, OnActionExecutionAsyncOnActionExecuted 메서드가 포함됩니다. 이러한 메서드는 지정된 작업을 위해 실행되는 필터를 래핑합니다.

  • OnActionExecuting는 작업 필터 이전에 실행됩니다.
  • OnActionExecuted는 작업의 모든 작업 필터 후에 실행됩니다.
  • OnActionExecutionAsync는 작업 필터 이전에 실행됩니다. next를 호출한 후의 코드는 작업의 필터 후에 실행됩니다.

다음 ControllerFiltersController 클래스:

  • 컨트롤러에 SampleActionFilterAttribute ([SampleActionFilter])를 적용합니다.
  • OnActionExecutingOnActionExecuted를 재정의합니다.
[SampleActionFilter]
public class ControllerFiltersController : Controller
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuting)}");

        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(OnActionExecuted)}");

        base.OnActionExecuted(context);
    }

    public IActionResult Index()
    {
        Console.WriteLine(
            $"- {nameof(ControllerFiltersController)}.{nameof(Index)}");

        return Content("Check the Console.");
    }
}

https://localhost:<port>/ControllerFilters로 이동하면 다음 코드가 실행됩니다.

  • ControllerFiltersController.OnActionExecuting
    • GlobalSampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • ControllerFiltersController.Index
      • SampleActionFilterAttribute.OnActionExecuted
    • GlobalSampleActionFilter.OnActionExecuted
  • ControllerFiltersController.OnActionExecuted

컨트롤러 수준 필터는 Order 속성을 int.MinValue로 설정합니다. 컨트롤러 수준 필터는 메서드에 적용된 후에 실행되도록 설정할 수 없습니다. 순서는 다음 섹션에 설명되어 있습니다.

Razor Pages의 경우 필터 메서드를 재정의하여 Razor Page 필터 구현을 참조하세요.

기본값 순서 재정의

IOrderedFilter를 구현하여 실행의 기본 순서를 재정의할 수 있습니다. IOrderedFilter은 실행 순서를 결정하는 데 범위보다 우선 순위가 높은 Order 속성을 노출합니다. 낮은 Order 값을 가진 필터는:

  • 더 높은 Order 값을 가진 필터 이전에 before 코드를 실행합니다.
  • 더 높은 Order 값을 가진 필터 이후에 after 코드를 실행합니다.

컨트롤러 수준 필터 예제에서 GlobalSampleActionFilter는 전역 범위를 가지므로 컨트롤러 범위가 있는 SampleActionFilterAttribute 전에 실행됩니다. SampleActionFilterAttribute를 먼저 실행하려면 순서를 int.MinValue로 설정합니다.

[SampleActionFilter(Order = int.MinValue)]
public class ControllerFiltersController : Controller
{
    // ...
}

전역 필터 GlobalSampleActionFilter를 먼저 실행하려면 이 필터의 Orderint.MinValue로 설정합니다.

builder.Services.AddControllersWithViews(options =>
{
    options.Filters.Add<GlobalSampleActionFilter>(int.MinValue);
});

취소 및 단락

필터 메서드에 제공되는 ResourceExecutingContext 매개 변수의 Result 속성을 설정하여 필터 파이프라인을 단락시킬 수 있습니다. 예를 들어 다음 리소스 필터는 파이프라인의 나머지 부분이 실행되지 못하도록 합니다.

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult
        {
            Content = nameof(ShortCircuitingResourceFilterAttribute)
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context) { }
}

다음 코드에서 [ShortCircuitingResourceFilter][ResponseHeader] 필터는 Index 작업 메서드를 대상으로 지정합니다. ShortCircuitingResourceFilterAttribute 필터:

  • 리소스 필터이고 ResponseHeaderAttribute는 작업 필터이기 때문에 먼저 실행됩니다.
  • 나머지 파이프라인을 단락시킵니다.

따라서 ResponseHeaderAttribute 필터는 Index 작업에 대해 절대 실행되지 않습니다. 이 동작은 ShortCircuitingResourceFilterAttribute가 먼저 실행되기 때문에 작업 메서드 수준에서 두 필터를 적용하더라도 동일합니다. 필터 형식으로 인해 ShortCircuitingResourceFilterAttribute가 먼저 실행됩니다.

[ResponseHeader("Filter-Header", "Filter Value")]
public class ShortCircuitingController : Controller
{
    [ShortCircuitingResourceFilter]
    public IActionResult Index() =>
        Content($"- {nameof(ShortCircuitingController)}.{nameof(Index)}");
}

종속성 주입

형식 또는 인스턴스별로 필터를 추가할 수 있습니다. 인스턴스가 추가되면 모든 요청에 해당 인스턴스가 사용됩니다. 형식이 추가되면 해당 필터는 형식으로 활성화됩니다. 형식으로 활성화된 필터는 다음을 의미합니다.

  • 각 요청에 대해 인스턴스가 만들어집니다.
  • 모든 생성자 종속성이 DI(종속성 주입)를 통해서 채워집니다.

특성으로 구현되고 컨트롤러 클래스 또는 작업 메서드에 직접 추가되는 필터에는 DI(종속성 주입)에서 제공하는 생성자 종속성이 없을 것입니다. 특성에 해당 생성자 매개 변수가 적용된 위치에 제공되어야 하므로 DI에서 생성자 종속성을 제공할 수 없습니다.

다음 필터는 DI에서 제공하는 생성자 종속성을 지원합니다.

이전 필터는 컨트롤러 또는 작업에 적용할 수 있습니다.

로거는 DI를 통해서 사용할 수 있습니다. 그러나 로깅 용도로만 필터를 만들거나 사용하지는 마세요. 기본 제공 프레임워크 로깅은 일반적으로 로깅에 필요한 기능을 제공합니다. 필터에 추가된 로깅은:

  • 비즈니스 도메인 문제 또는 필터 고유의 동작에 중점을 두어야 합니다.
  • 작업 또는 다른 프레임워크 이벤트를 로깅해서는 안 됩니다. 기본 제공 필터는 작업 및 프레임워크 이벤트를 미리 로그합니다.

ServiceFilterAttribute

서비스 필터 구현 형식은 Program.cs에 등록됩니다. ServiceFilterAttribute는 DI에서 필터의 인스턴스를 검색합니다.

다음 코드는 DI를 사용하는 LoggingResponseHeaderFilterService 클래스를 보여줍니다.

public class LoggingResponseHeaderFilterService : IResultFilter
{
    private readonly ILogger _logger;

    public LoggingResponseHeaderFilterService(
            ILogger<LoggingResponseHeaderFilterService> logger) =>
        _logger = logger;

    public void OnResultExecuting(ResultExecutingContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuting)}");

        context.HttpContext.Response.Headers.Add(
            nameof(OnResultExecuting), nameof(LoggingResponseHeaderFilterService));
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        _logger.LogInformation(
            $"- {nameof(LoggingResponseHeaderFilterService)}.{nameof(OnResultExecuted)}");
    }
}

다음 코드에서 LoggingResponseHeaderFilterService는 DI 컨테이너에 추가됩니다.

builder.Services.AddScoped<LoggingResponseHeaderFilterService>();

다음 코드에서 ServiceFilter 특성은 DI에서 LoggingResponseHeaderFilterService 필터의 인스턴스를 검색합니다.

[ServiceFilter(typeof(LoggingResponseHeaderFilterService))]
public IActionResult WithServiceFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithServiceFilter)}");

ServiceFilterAttribute를 사용하는 경우 ServiceFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 외부에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 다음을 보장하지 않습니다.
    • 필터의 단일 인스턴스 생성.
    • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.
  • 싱글톤 이외의 수명이 지정된 서비스에 의존하는 필터와 함께 사용하지 마세요.

ServiceFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 DI에서 지정된 형식을 로드합니다.

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute와 비슷하지만 해당 형식은 DI 컨테이너에서 직접 해결되지 않습니다. Microsoft.Extensions.DependencyInjection.ObjectFactory를 사용하여 형식을 인스턴스화합니다.

TypeFilterAttribute 형식은 DI 컨테이너에서 직접 해결되지 않기 때문입니다.

  • TypeFilterAttribute을 사용하여 참조되는 형식은 DI 컨테이너를 사용하여 등록할 필요가 없습니다. DI 컨테이너에서 충족하는 종속성을 갖고 있습니다.
  • TypeFilterAttribute는 형식에 대한 생성자 인수를 필요에 따라 받을 수 있습니다.

TypeFilterAttribute를 사용하는 경우 TypeFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 밖에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 단일 필터 인스턴스가 생성되도록 보장하지 않습니다.

  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

다음 예제는 TypeFilterAttribute를 사용하여 형식에 인수를 전달하는 방법을 보여줍니다.

[TypeFilter(typeof(LoggingResponseHeaderFilter),
    Arguments = new object[] { "Filter-Header", "Filter Value" })]
public IActionResult WithTypeFilter() =>
    Content($"- {nameof(FilterDependenciesController)}.{nameof(WithTypeFilter)}");

권한 부여 필터

권한 부여 필터는:

  • 필터 파이프라인에서 첫 번째로 실행되는 필터입니다.
  • 작업 메서드에 대한 액세스를 제어합니다.
  • before 메서드는 있지만 after 메서드는 없습니다.

사용자 지정 권한 부여 필터는 사용자 지정 권한 부여 프레임워크를 필요로 합니다. 사용자 지정 필터를 작성하는 대신 권한 부여 정책을 구성하거나 사용자 지정 권한 부여 정책을 작성하는 것이 좋습니다. 기본 제공 권한 부여 필터는:

  • 권한 부여 시스템을 호출합니다.
  • 요청을 승인하지 않습니다.

권한 부여 필터 내에서 예외를 던지지 마세요.

  • 해당 예외는 처리되지 않습니다.
  • 예외 필터가 해당 예외를 처리하지 않습니다.

권한 부여 필터에서 예외가 발생할 경우 챌린지 발행을 고려하세요.

권한 부여에 대해 자세히 알아봅니다.

리소스 필터

리소스 필터는:

리소스 필터는 파이프라인의 대부분을 단락시켜야 하는 경우에 유용합니다. 예를 들어 캐싱 필터는 캐시 적중 시 파이프라인의 나머지 부분을 막을 수 있습니다.

리소스 필터 예제:

작업 필터

작업 필터는 Razor Pages에 적용되지 않습니다. Razor Pages는 IPageFilterIAsyncPageFilter를 지원합니다. 자세한 내용은 Razor Pages에 대한 필터 메서드를 참조하세요.

작업 필터는:

다음 코드는 예제 작업 필터를 보여 줍니다.

public class SampleActionFilter : IActionFilter
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
    }
}

ActionExecutingContext는 다음과 같은 속성을 제공합니다.

  • ActionArguments - 작업 메서드에 대한 입력을 읽을 수 있습니다.
  • Controller - 컨트롤러 인스턴스를 조작할 수 있습니다.
  • Result - Result 설정은 작업 메서드 및 후속 작업 필터의 실행을 단락시킵니다.

작업 메서드에서 예외를 던지면:

  • 후속 필터의 실행을 막습니다.
  • Result 설정과 달리 성공적인 결과 대신 실패로 처리됩니다.

ActionExecutedContextControllerResult에 더하여 다음과 같은 속성을 제공합니다.

  • Canceled - 작업 실행이 다른 필터에 의해 단락된 경우 true입니다.
  • Exception - 작업 또는 이전에 실행된 작업 필터에서 예외가 던져진 경우 null이 아닙니다. 이 속성을 null로 설정:
    • 예외를 효과적으로 처리합니다.
    • Result는 작업 메서드에서 반환되는 것처럼 실행됩니다.

IAsyncActionFilter의 경우 ActionExecutionDelegate에 대한 호출은:

  • 후속 작업 필터 및 작업 메서드를 실행합니다.
  • ActionExecutedContext을(를) 반환합니다.

단락시키려면 Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result을 결과 인스턴스에 할당하고 next(ActionExecutionDelegate)를 호출하지 않아야 합니다.

프레임워크는 서브클래싱 할 수 있는 추상 ActionFilterAttribute를 제공합니다.

OnActionExecuting 작업 필터는 다음에 사용할 수 있습니다.

  • 모델 상태의 유효성을 검사합니다.
  • 상태가 유효하지 않은 경우 오류를 반환합니다.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(context.ModelState);
        }
    }
}

참고 항목

[ApiController] 특성으로 주석이 추가된 컨트롤러는 모델 상태의 유효성을 자동으로 검사하고 400 응답을 반환합니다. 자세한 정보는 자동 HTTP 400 응답을 참조하세요.

OnActionExecuted 메서드는 작업 메서드 이후에 실행됩니다.

  • 그리고 Result 속성을 통해 작업 결과를 확인하고 조작할 수 있습니다.
  • Canceled는 작업 실행이 다른 필터에 의해 단락된 경우 true로 설정됩니다.
  • Exception은 작업 또는 후속 작업 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. null로 설정 Exception :
    • 예외를 효과적으로 처리합니다.
    • ActionExecutedContext.Result은 작업 메서드에서 정상적으로 반환되는 것처럼 실행됩니다.

예외 필터

예외 필터:

다음 샘플 예외 필터는 앱이 개발 중일 때 발생하는 예외에 대한 세부 정보를 표시합니다.

public class SampleExceptionFilter : IExceptionFilter
{
    private readonly IHostEnvironment _hostEnvironment;

    public SampleExceptionFilter(IHostEnvironment hostEnvironment) =>
        _hostEnvironment = hostEnvironment;

    public void OnException(ExceptionContext context)
    {
        if (!_hostEnvironment.IsDevelopment())
        {
            // Don't display exception details unless running in Development.
            return;
        }

        context.Result = new ContentResult
        {
            Content = context.Exception.ToString()
        };
    }
}

다음 코드에서는 예외 필터를 테스트합니다.

[TypeFilter(typeof(SampleExceptionFilter))]
public class ExceptionController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(ExceptionController)}.{nameof(Index)}");
}

예외 필터:

  • before 및 after 이벤트가 없습니다.
  • OnException 또는 OnExceptionAsync를 구현합니다.
  • Razor Page 또는 컨트롤러 생성,모델 바인딩, 작업 필터 또는 작업 메서드에서 발생하는 처리되지 않은 예외를 처리합니다.
  • 리소스 필터, 결과 필터 또는 MVC 결과 실행에서 발생하는 예외를 잡지 않습니다.

예외를 처리하려면 ExceptionHandled 속성을 true로 설정하거나 Result 속성을 할당합니다. 그러면 예외가 전파되지 않습니다. 예외 필터는 예외를 “성공”으로 변환할 수 없습니다. 이는 작업 필터에서만 가능합니다.

예외 필터:

  • 작업 내에서 발생하는 예외를 잡는 데 좋습니다.
  • 오류 처리 미들웨어만큼 유연하지 않습니다.

예외 처리의 경우 미들웨어를 선호합니다. 어떤 작업 메서드가 호출되는지에 따라 오류 처리 방식이 ‘다른’ 경우에만 예외 필터를 사용합니다. 예를 들어 앱에는 API 엔드포인트 및 보기/HTML 모두에 대한 작업 메서드가 있을 수 있습니다. API 엔드포인트는 JSON으로 오류 정보를 반환할 수 있습니다. 반면 보기 기반 작업은 HTML로 오류 페이지를 반환할 수 있습니다.

결과 필터

결과 필터는:

IResultFilter 및 IAsyncResultFilter

다음 코드는 샘플 작업 필터를 보여 줍니다.

public class SampleResultFilter : IResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        // Do something before the result executes.
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Do something after the result executes.
    }
}

실행되는 결과의 종류는 작업에 따라 다릅니다. 보기를 반환하는 작업에는 실행 중인 ViewResult의 일부로 모든 Razor 프로세스가 포함됩니다. API 메서드는 실행 결과의 일부로 일부 serialization을 수행할 수 있습니다. 작업 결과에 대해 자세히 알아보세요.

결과 필터는 작업 또는 작업 필터가 작업 결과를 생성하는 경우에만 실행됩니다. 다음 경우에는 결과 필터가 실행되지 않습니다.

  • 권한 부여 필터 또는 리소스 필터가 파이프라인을 단락시킬 경우.
  • 예외 필터는 작업 결과를 생성하여 예외를 처리합니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 메서드는 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Canceltrue로 설정하여 작업 결과 및 후속 결과 필터를 단락시킬 수 있습니다. 단락시킬 경우 빈 응답을 생성하지 않도록 응답 개체에 작성하세요. IResultFilter.OnResultExecuting에서 예외 throw:

  • 작업 결과 및 후속 필터의 실행을 막습니다.
  • 성공적인 결과 대신 실패로 처리됩니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 메서드가 실행될 때에는 응답이 이미 클라이언트에 전송되었을 수 있습니다. 이미 클라이언트에 전송된 응답은 변경할 수 없습니다.

ResultExecutedContext.Canceled는 작업 결과 실행이 다른 필터에 의해 단락(short-circuit) 처리된 경우 true로 설정됩니다.

ResultExecutedContext.Exception은 작업 결과 또는 후속 결과 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. Exception을 효과적으로 null로 설정하면 예외를 '처리'하고 예외가 파이프라인의 뒷부분에서 다시 throw되지 않습니다. 결과 필터에서 예외를 처리하는 경우 데이터를 응답에 쓸 수 있는 신뢰할 수 있는 방법이 없습니다. 작업 결과가 예외를 던질 때 헤더가 클라이언트에 플러시된 경우 오류 코드를 전송하기 위한 신뢰할 수 있는 메커니즘이 없습니다.

IAsyncResultFilter의 경우 ResultExecutionDelegateawait next 호출은 후속 결과 필터 및 작업 결과를 실행합니다. 단락(short-circuit) 처리하려면 ResultExecutingContext.Canceltrue로 설정하고 ResultExecutionDelegate를 호출하지 않습니다.

public class SampleAsyncResultFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(
        ResultExecutingContext context, ResultExecutionDelegate next)
    {
        if (context.Result is not EmptyResult)
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }
    }
}

프레임워크는 서브클래싱 할 수 있는 추상 ResultFilterAttribute를 제공합니다. 이전에 표시된 ResponseHeaderAttribute 클래스는 결과 필터 특성의 예입니다.

IAlwaysRunResultFilter 및 IAsyncAlwaysRunResultFilter

IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter 인터페이스는 모든 작업 결과에 대해 실행되는 IResultFilter 구현을 선언합니다. 여기에는 다음에 의해 생성되는 작업 결과가 포함됩니다.

  • 단락하는 권한 부여 필터 및 리소스 필터
  • 예외 필터

예를 들어 다음 필터는 항상 실행되어 콘텐츠 협상이 실패할 경우 작업 결과(ObjectResult)를 422 Unprocessable Entity 상태 코드로 설정합니다.

public class UnprocessableResultFilter : IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult
            && statusCodeResult.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Unprocessable")
            {
                StatusCode = StatusCodes.Status422UnprocessableEntity
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context) { }
}

IFilterFactory

IFilterFactoryIFilterMetadata를 구현합니다. 따라서 필터 파이프라인 어디에서나 IFilterFactory 인스턴스를 IFilterMetadata 인스턴스로 사용할 수 있습니다. 런타임이 필터를 호출하려고 준비할 때 IFilterFactory로 캐스팅을 시도합니다. 해당 캐스팅에 성공하면 CreateInstance 메서드를 호출하여 호출되는 IFilterMetadata 인스턴스를 만듭니다. 앱이 시작될 때 정확한 필터 파이프라인을 명시적으로 설정할 필요가 없으므로 유연한 디자인을 제공합니다.

IFilterFactory.IsReusable:

  • 팩터리에서 만든 필터 인스턴스가 생성된 요청 범위 외부에서 재사용될 수 있다는 힌트입니다.
  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

ASP.NET Core 런타임은 다음을 보장하지 않습니다.

  • 필터의 단일 인스턴스 생성.
  • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.

Warning

필터의 원본이 명확하고, 필터가 상태 비저장이며, 필터가 여러 HTTP 요청에서 안전하게 사용할 수 있는 경우에만 true를 반환하도록 IFilterFactory.IsReusable을 구성합니다. 예를 들어 IFilterFactory.IsReusabletrue를 반환하는 경우 범위가 지정되거나 일시적으로 등록된 DI에서 필터를 반환하지 마세요.

필터를 만드는 다른 방법으로 사용자 지정 특성 구현을 사용하여 IFilterFactory를 구현할 수 있습니다.

public class ResponseHeaderFilterFactory : Attribute, IFilterFactory
{
    public bool IsReusable => false;

    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider) =>
        new InternalResponseHeaderFilter();

    private class InternalResponseHeaderFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context) =>
            context.HttpContext.Response.Headers.Add(
                nameof(OnActionExecuting), nameof(InternalResponseHeaderFilter));

        public void OnActionExecuted(ActionExecutedContext context) { }
    }

필터는 다음 코드에서 적용됩니다.

[ResponseHeaderFilterFactory]
public IActionResult Index() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(Index)}");

특성에서 구현된 IFilterFactory

IFilterFactory를 구현하는 필터는 다음과 같은 필터에 유용합니다.

  • 매개 변수 전달을 필요로 하지 않는 필터.
  • DI에 의해 채워져야 할 생성자 종속성이 있는 필터.

TypeFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 서비스 컨테이너(DI)에서 지정된 형식을 로드합니다.

public class SampleActionTypeFilterAttribute : TypeFilterAttribute
{
    public SampleActionTypeFilterAttribute()
         : base(typeof(InternalSampleActionFilter)) { }

    private class InternalSampleActionFilter : IActionFilter
    {
        private readonly ILogger<InternalSampleActionFilter> _logger;

        public InternalSampleActionFilter(ILogger<InternalSampleActionFilter> logger) =>
            _logger = logger;

        public void OnActionExecuting(ActionExecutingContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuting)}");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation(
                $"- {nameof(InternalSampleActionFilter)}.{nameof(OnActionExecuted)}");
        }
    }
}

다음 코드에서는 필터를 적용하는 세 가지 방법을 보여 줍니다.

[SampleActionTypeFilter]
public IActionResult WithDirectAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithDirectAttribute)}");

[TypeFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithTypeFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithTypeFilterAttribute)}");

[ServiceFilter(typeof(SampleActionTypeFilterAttribute))]
public IActionResult WithServiceFilterAttribute() =>
    Content($"- {nameof(FilterFactoryController)}.{nameof(WithServiceFilterAttribute)}");

이전 코드에서는 필터를 적용하는 첫 번째 방법을 사용하는 것이 좋습니다.

필터 파이프라인에서 미들웨어 사용

리소스 필터는 파이프라인의 뒷부분에 제공되는 모든 실행을 둘러싼다는 점에서 미들웨어처럼 작동합니다. 하지만 필터는 런타임의 일부라는 점에서 미들웨어와 다릅니다. 즉, 컨텍스트 및 구문에 액세스할 수 있습니다.

미들웨어를 필터로 사용하려면 필터 파이프라인에 삽입할 미들웨어를 지정하는 Configure 메서드를 포함한 형식을 만듭니다. 다음 예제에서는 미들웨어를 사용하여 응답 헤더를 설정합니다.

public class FilterMiddlewarePipeline
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            context.Response.Headers.Add("Pipeline", "Middleware");

            await next();
        });
    }
}

MiddlewareFilterAttribute를 사용하여 미들웨어를 실행합니다.

[MiddlewareFilter(typeof(FilterMiddlewarePipeline))]
public class FilterMiddlewareController : Controller
{
    public IActionResult Index() =>
        Content($"- {nameof(FilterMiddlewareController)}.{nameof(Index)}");
}

미들웨어 필터는 모델 바인딩 이전 및 나머지 파이프라인 이후에 리소스 필터와 동일한 필터 파이프라인 단계에서 실행됩니다.

스레드로부터의 안전성

Type 대신 필터의 인스턴스Add에 전달하는 경우 필터는 싱글톤이며 스레드로부터 안전하지 않습니다.

추가 리소스

작성자: Kirk Larkin, Rick Anderson, Tom DykstraSteve Smith

ASP.NET Core에서 필터를 사용하면 요청 처리 파이프라인의 특정 단계 전후에 코드를 실행할 수 있습니다.

기본 제공 필터는 다음과 같은 작업을 처리합니다.

  • 권한 부여(사용자가 권한이 없는 리소스에 액세스하는 것을 방지).
  • 응답 캐싱(요청 파이프라인을 단락하여 캐시된 응답 반환).

사용자 지정 필터를 만들어 횡단 관심사를 처리할 수 있습니다. 횡단 관심사의 사례로는 오류 처리, 캐싱, 구성, 권한 부여 및 로깅을 들 수 있습니다. 필터는 코드 중복을 방지합니다. 예를 들어 오류 처리 예외 필터는 오류 처리를 통합할 수 있습니다.

이 문서는 Razor Pages, API 컨트롤러 및 보기를 사용하는 컨트롤러에 적용됩니다. 필터는 작동 시 Razor 구성 요소에 직접적인 영향을 주지 않습니다. 필터는 다음과 같은 경우에만 간접적으로 구성 요소에 영향을 줄 수 있습니다.

  • 구성 요소가 페이지 또는 보기에 포함되어 있는 경우
  • 페이지 또는 컨트롤러 및 보기에서 필터를 사용하는 경우

예제 살펴보기 및 다운로드(다운로드 방법)

필터 작동 방법

필터는 ‘필터 파이프라인’이라고도 하는 ‘ASP.NET Core 동작 호출 파이프라인’내에서 실행됩니다. 필터 파이프라인은 ASP.NET Core가 실행할 작업을 선택한 후에 실행됩니다.

The request is processed through Other Middleware, Routing Middleware, Action Selection, and the Action Invocation Pipeline. The request processing continues back through Action Selection, Routing Middleware, and various Other Middleware before becoming a response sent to the client.

필터 유형

각 필터 형식은 필터 파이프라인의 다른 단계에서 실행됩니다.

  • 권한 부여 필터는 가장 먼저 실행되고 사용자가 요청에 대한 권한이 있는지 여부를 결정하는 데 사용됩니다. 권한 부여 필터는 요청이 인증되지 않는 경우 파이프라인을 단락시킵니다.

  • 리소스 필터:

    • 권한 부여 후 실행됩니다.
    • OnResourceExecuting는 나머지 필터 파이프라인보다 먼저 코드를 실행합니다. 예를 들어 OnResourceExecuting는 모델 바인딩 전에 코드를 실행합니다.
    • OnResourceExecuted는 파이프라인의 나머지 부분이 완료된 후에 코드를 실행합니다.
  • 작업 필터:

    • 작업 메서드가 호출되기 전후에 즉시 코드를 실행합니다.
    • 작업에 전달된 인수를 변경할 수 있습니다.
    • 작업에서 반환된 결과를 변경할 수 있습니다.
    • Razor Pages에서 지원되지 않습니다.
  • 예외 필터는 응답 본문이 쓰여지기 전에 발생한 처리되지 않은 예외에 대한 전역 정책을 적용합니다.

  • 결과 필터는 작업 결과의 실행 전후에 즉시 코드를 실행합니다. 작업 메서드가 성공적으로 실행되는 경우에만 실행됩니다. 보기 또는 포맷터 실행을 둘러싸야 하는 논리에 유용합니다.

다음 다이어그램은 필터 형식이 필터 파이프라인에서 상호 작용하는 방법을 보여줍니다.

The request is processed through Authorization Filters, Resource Filters, Model Binding, Action Filters, Action Execution and Action Result Conversion, Exception Filters, Result Filters, and Result Execution. On the way out, the request is only processed by Result Filters and Resource Filters before becoming a response sent to the client.

구현

필터는 서로 다른 인터페이스 정의를 통해 동기 및 비동기 구현을 모두 지원합니다.

동기 필터는 해당 파이프라인 단계 전후에 코드를 실행합니다. 예를 들어 OnActionExecuting는 작업 메서드가 호출되기 전에 호출됩니다. OnActionExecuted는 작업 메서드가 반환된 후 호출됩니다.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

위의 코드에서 MyDebug샘플 다운로드의 유틸리티 함수입니다.

비동기 필터는 On-Stage-ExecutionAsync 메서드를 정의합니다. 예: OnActionExecutionAsync

public class SampleAsyncActionFilter : IAsyncActionFilter
{
    public async Task OnActionExecutionAsync(
        ActionExecutingContext context,
        ActionExecutionDelegate next)
    {
        // Do something before the action executes.

        // next() calls the action method.
        var resultContext = await next();
        // resultContext.Result is set.
        // Do something after the action executes.
    }
}

이전 코드에서 SampleAsyncActionFilter에는 작업 메서드를 실행하는 ActionExecutionDelegate(next)가 포함되어 있습니다.

여러 필터 단계

단일 클래스에서 여러 필터 단계에 대한 인터페이스를 구현할 수 있습니다. 예를 들어 ActionFilterAttribute를 구현합니다.

필터 인터페이스의 동기 또는 비동기 버전을 모두 구현하지 말고 그 중 한 가지만 구현하세요. 런타임은 먼저 필터가 비동기 인터페이스를 구현하는지를 확인하고 그렇다면 이를 호출합니다. 그렇지 않으면 동기 인터페이스의 메서드를 호출합니다. 비동기 및 동기 인터페이스가 모두 하나의 클래스에 구현된 경우에는 비동기 메서드만 호출됩니다. ActionFilterAttribute 같은 추상 클래스를 사용하는 경우 각 필터 형식에 대한 동기 메서드 또는 비동기 메서드만 재정의합니다.

기본 제공 필터 특성

ASP.NET Core에는 서브클래싱 및 사용자 지정할 수 있는 기본 제공 특성 기반 필터가 포함되어 있습니다. 예를 들어 다음 결과 필터는 응답에 헤더를 추가합니다.

public class AddHeaderAttribute : ResultFilterAttribute
{
    private readonly string _name;
    private readonly string _value;

    public AddHeaderAttribute(string name, string value)
    {
        _name = name;
        _value = value;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add( _name, new string[] { _value });
        base.OnResultExecuting(context);
    }
}

특성을 사용하면 이전 예제와 같이 필터에서 인수를 받을 수 있습니다. 컨트롤러나 작업 메서드에 AddHeaderAttribute를 적용하고 HTTP 헤더의 이름 및 값을 지정합니다.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

브라우저 개발자 도구와 같은 도구를 사용하여 헤더를 검사합니다. 응답 헤더author: Rick Anderson이 표시됩니다.

다음 코드는 다음과 같은 작업을 수행하는 ActionFilterAttribute를 구현합니다.

  • 구성 시스템에서 제목과 이름을 읽습니다. 앞의 샘플과 달리 다음 코드는 필터 매개 변수를 코드에 추가하지 않아도 됩니다.
  • 응답 헤더에 제목과 이름을 추가합니다.
public class MyActionFilterAttribute : ActionFilterAttribute
{
    private readonly PositionOptions _settings;

    public MyActionFilterAttribute(IOptions<PositionOptions> options)
    {
        _settings = options.Value;
        Order = 1;
    }

    public override void OnResultExecuting(ResultExecutingContext context)
    {
        context.HttpContext.Response.Headers.Add(_settings.Title, 
                                                 new string[] { _settings.Name });
        base.OnResultExecuting(context);
    }
}

구성 옵션은 옵션 패턴을 사용하여 구성 시스템에서 제공됩니다. 예를 들어 appsettings.json 파일에서 다음을 수행합니다.

{
  "Position": {
    "Title": "Editor",
    "Name": "Joe Smith"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}

StartUp.ConfigureServices에서

  • PositionOptions 클래스는 "Position" 구성 영역을 사용하여 서비스 컨테이너에 추가됩니다.
  • MyActionFilterAttribute는 서비스 컨테이너에 추가됩니다.
public void ConfigureServices(IServiceCollection services)
{
    services.Configure<PositionOptions>(
             Configuration.GetSection("Position"));
    services.AddScoped<MyActionFilterAttribute>();

    services.AddControllersWithViews();
}

다음 코드에서는 PositionOptions 클래스를 보여 줍니다.

public class PositionOptions
{
    public string Title { get; set; }
    public string Name { get; set; }
}

다음 코드는 MyActionFilterAttributeIndex2 메서드에 적용합니다.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

Sample/Index2 엔드포인트가 호출될 때 응답 헤더author: Rick AndersonEditor: Joe Smith가 표시됩니다.

다음 코드는 MyActionFilterAttributeAddHeaderAttribute를 Razor Page에 적용합니다.

[AddHeader("Author", "Rick Anderson")]
[ServiceFilter(typeof(MyActionFilterAttribute))]
public class IndexModel : PageModel
{
    public void OnGet()
    {
    }
}

Razor Page 처리기 메서드에는 필터를 적용할 수 없습니다. Razor Page 모델 또는 전역적으로 적용할 수 있습니다.

여러 필터 인터페이스에는 사용자 지정 구현에 대한 기본 클래스로 사용할 수 있는 해당 특성이 있습니다.

필터 특성:

필터 범위 및 실행 순서

세 가지 범위 중 하나에서 필터를 파이프라인에 추가할 수 있습니다.

  • 컨트롤러 작업에서 특성 사용. 필터 특성은 Razor Pages 처리기 메서드에 적용할 수 없습니다.
  • 컨트롤러 또는 Razor Page에서 특성 사용.
  • 다음 코드와 같이 모든 컨트롤러, 작업 및 Razor Pages에 전역으로 사용:
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

기본 실행 순서

파이프라인의 특정 단계에 여러 개의 필터가 있는 경우 범위가 기본 필터 실행 순서를 결정합니다. 전역 필터는 메서드 필터를 둘러싼 클래스 필터를 둘러쌉니다.

필터 중첩의 결과로 필터의 after 코드는 before 코드의 역순으로 실행됩니다. 필터의 순서는 다음과 같습니다.

  • 전역 필터의 before 코드.
    • 컨트롤러 및 Razor Page 필터의 before 코드.
      • 작업 메서드 필터의 before 코드.
      • 작업 메서드 필터의 after 코드.
    • 컨트롤러 및 Razor Page 필터의 after 코드.
  • 전역 필터의 after 코드.

다음 예제는 필터 메서드가 동기 작업 필터에 대해 호출되는 순서를 보여줍니다.

Sequence 필터 범위 필터 메서드
1 전역 OnActionExecuting
2 컨트롤러 또는 Razor Page OnActionExecuting
3 메서드 OnActionExecuting
4 메서드 OnActionExecuted
5 컨트롤러 또는 Razor Page OnActionExecuted
6 전역 OnActionExecuted

컨트롤러 수준 필터

Controller 기본 클래스에서 상속되는 모든 컨트롤러에는 Controller.OnActionExecuting, Controller.OnActionExecutionAsyncController.OnActionExecutedOnActionExecuted 메서드가 포함됩니다. 다음 메서드는

  • 지정된 작업을 위해 실행되는 필터를 래핑합니다.
  • OnActionExecuting는 작업 필터 이전에 호출됩니다.
  • OnActionExecuted는 모든 작업 필터 이후에 호출됩니다.
  • OnActionExecutionAsync는 작업 필터 이전에 호출됩니다. 작업 메서드 이후 next가 실행된 후의 필터 코드.

예를 들어 다운로드 예제에서 MySampleActionFilter은 시작할 때 전역적으로 적용됩니다.

TestController :

  • SampleActionFilterAttribute([SampleActionFilter])를 FilterTest2 작업에 적용합니다.
  • OnActionExecutingOnActionExecuted를 재정의합니다.
public class TestController : Controller
{
    [SampleActionFilter(Order = int.MinValue)]
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

MyDisplayRouteInfoRick.Docs.Samples.RouteInfo NuGet 패키지가 제공하며 경로 정보를 표시합니다.

https://localhost:5001/Test/FilterTest2로 이동하면 다음 코드가 실행됩니다.

  • TestController.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • SampleActionFilterAttribute.OnActionExecuting
        • TestController.FilterTest2
      • SampleActionFilterAttribute.OnActionExecuted
    • MySampleActionFilter.OnActionExecuted
  • TestController.OnActionExecuted

컨트롤러 수준 필터는 Order 속성을 int.MinValue로 설정합니다. 컨트롤러 수준 필터는 메서드에 적용된 후에 실행되도록 설정할 수 없습니다. 순서는 다음 섹션에 설명되어 있습니다.

Razor Pages의 경우 필터 메서드를 재정의하여 Razor Page 필터 구현을 참조하세요.

기본 순서 재정의

IOrderedFilter를 구현하여 실행의 기본 순서를 재정의할 수 있습니다. IOrderedFilter은 실행 순서를 결정하는 데 범위보다 우선 순위가 높은 Order 속성을 노출합니다. 낮은 Order 값을 가진 필터는:

  • 더 높은 Order 값을 가진 필터 이전에 before 코드를 실행합니다.
  • 더 높은 Order 값을 가진 필터 이후에 after 코드를 실행합니다.

Order 속성은 생성자 매개 변수를 사용하여 설정됩니다.

[SampleActionFilter(Order = int.MinValue)]

다음 컨트롤러의 두 작업 필터를 고려합니다.

[MyAction2Filter]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

전역 필터는 StartUp.ConfigureServices에 추가됩니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter));
    });
}

3개의 필터는 다음 순서로 실행됩니다.

  • Test2Controller.OnActionExecuting
    • MySampleActionFilter.OnActionExecuting
      • MyAction2FilterAttribute.OnActionExecuting
        • Test2Controller.FilterTest2
      • MyAction2FilterAttribute.OnResultExecuting
    • MySampleActionFilter.OnActionExecuted
  • Test2Controller.OnActionExecuted

Order 속성은 필터가 실행되는 순서를 결정할 때 범위를 무시합니다. 필터가 순서에 따라 먼저 정렬된 다음, 범위는 연결을 끊는 데 사용됩니다. 모든 기본 제공 필터는 IOrderedFilter을 구현하고 기본 Order 값을 0으로 설정합니다. 앞서 언급했듯이 컨트롤러 수준 필터는 Order 속성을 int.MinValue로 설정합니다. 기본 제공 필터의 경우 Order가 0이 아닌 값으로 설정되지 않은 한 범위가 순서를 결정합니다.

이전 코드에서 MySampleActionFilter는 전역 범위를 가지므로 컨트롤러 범위를 포함하는 MyAction2FilterAttribute 전에 실행됩니다. MyAction2FilterAttribute를 먼저 실행하려면 순서를 int.MinValue로 설정합니다.

[MyAction2Filter(int.MinValue)]
public class Test2Controller : Controller
{
    public IActionResult FilterTest2()
    {
        return ControllerContext.MyDisplayRouteInfo();
    }

    public override void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuting(context);
    }

    public override void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), HttpContext.Request.Path);
        base.OnActionExecuted(context);
    }
}

전역 필터 MySampleActionFilter를 먼저 실행하려면 Orderint.MinValue로 설정합니다.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllersWithViews(options =>
   {
        options.Filters.Add(typeof(MySampleActionFilter),
                            int.MinValue);
    });
}

취소 및 단락

필터 메서드에 제공되는 ResourceExecutingContext 매개 변수의 Result 속성을 설정하여 필터 파이프라인을 단락시킬 수 있습니다. 예를 들어 다음 리소스 필터는 파이프라인의 나머지 부분이 실행되지 않도록 막습니다.

public class ShortCircuitingResourceFilterAttribute : Attribute, IResourceFilter
{
    public void OnResourceExecuting(ResourceExecutingContext context)
    {
        context.Result = new ContentResult()
        {
            Content = "Resource unavailable - header not set."
        };
    }

    public void OnResourceExecuted(ResourceExecutedContext context)
    {
    }
}

다음 코드에서 ShortCircuitingResourceFilterAddHeader 필터는 SomeResource 작업 메서드를 대상으로 지정합니다. ShortCircuitingResourceFilter :

  • 리소스 필터이고 AddHeader는 작업 필터이기 때문에 먼저 실행됩니다.
  • 나머지 파이프라인을 단락시킵니다.

따라서 AddHeader 필터는 SomeResource 작업에 대해 절대 실행되지 않습니다. 이 동작은 ShortCircuitingResourceFilter가 먼저 실행되기 때문에 작업 메서드 수준에서 두 필터를 적용하더라도 동일합니다. 해당 필터 형식 또는 Order 속성의 명시적 사용으로 인해 ShortCircuitingResourceFilter가 먼저 실행됩니다.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

종속성 주입

형식 또는 인스턴스별로 필터를 추가할 수 있습니다. 인스턴스가 추가되면 모든 요청에 해당 인스턴스가 사용됩니다. 형식이 추가되면 해당 필터는 형식으로 활성화됩니다. 형식으로 활성화된 필터는 다음을 의미합니다.

  • 각 요청에 대해 인스턴스가 만들어집니다.
  • 모든 생성자 종속성이 DI(종속성 주입)를 통해서 채워집니다.

특성으로 구현되고 컨트롤러 클래스 또는 작업 메서드에 직접 추가되는 필터에는 DI(종속성 주입)에서 제공하는 생성자 종속성이 없을 것입니다. DI는 다음과 같은 이유로 생성자 종속성을 제공할 수 없습니다.

  • 특성이 적용될 때 해당 생성자 매개 변수가 제공되어야 하기 때문입니다.
  • 특성이 작동하는 방법의 제한 사항입니다.

다음 필터는 DI에서 제공하는 생성자 종속성을 지원합니다.

위의 필터는 컨트롤러 또는 작업 메서드에 적용할 수 있습니다.

로거는 DI를 통해서 사용할 수 있습니다. 그러나 로깅 용도로만 필터를 만들거나 사용하지는 마세요. 기본 제공 프레임워크 로깅은 일반적으로 로깅에 필요한 기능을 제공합니다. 필터에 추가된 로깅은:

  • 비즈니스 도메인 문제 또는 필터 고유의 동작에 중점을 두어야 합니다.
  • 작업 또는 다른 프레임워크 이벤트를 로깅해서는 안 됩니다. 기본 제공 필터는 작업 및 프레임워크 이벤트를 로깅합니다.

ServiceFilterAttribute

서비스 필터 구현 형식은 ConfigureServices에 등록됩니다. ServiceFilterAttribute는 DI에서 필터의 인스턴스를 검색합니다.

다음 코드는 AddHeaderResultServiceFilter를 보여 줍니다.

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

다음 코드에서 AddHeaderResultServiceFilter는 DI 컨테이너에 추가됩니다.

public void ConfigureServices(IServiceCollection services)
{
    // Add service filters.
    services.AddScoped<AddHeaderResultServiceFilter>();
    services.AddScoped<SampleActionFilterAttribute>();

    services.AddControllersWithViews(options =>
   {
       options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader",
           "Result filter added to MvcOptions.Filters"));         // An instance
        options.Filters.Add(typeof(MySampleActionFilter));         // By type
        options.Filters.Add(new SampleGlobalActionFilter());       // An instance
    });
}

다음 코드에서 ServiceFilter 특성은 DI에서 AddHeaderResultServiceFilter 필터의 인스턴스를 검색합니다.

[ServiceFilter(typeof(AddHeaderResultServiceFilter))]
public IActionResult Index()
{
    return View();
}

ServiceFilterAttribute를 사용하는 경우 ServiceFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 외부에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 다음을 보장하지 않습니다.

    • 필터의 단일 인스턴스 생성.
    • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.
  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

ServiceFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 DI에서 지정된 형식을 로드합니다.

TypeFilterAttribute

TypeFilterAttributeServiceFilterAttribute와 비슷하지만 해당 형식은 DI 컨테이너에서 직접 해결되지 않습니다. Microsoft.Extensions.DependencyInjection.ObjectFactory를 사용하여 형식을 인스턴스화합니다.

TypeFilterAttribute 형식은 DI 컨테이너에서 직접 해결되지 않기 때문입니다.

  • TypeFilterAttribute을 사용하여 참조되는 형식은 DI 컨테이너를 사용하여 등록할 필요가 없습니다. DI 컨테이너에서 충족하는 종속성을 갖고 있습니다.
  • TypeFilterAttribute는 형식에 대한 생성자 인수를 필요에 따라 받을 수 있습니다.

TypeFilterAttribute를 사용하는 경우 TypeFilterAttribute.IsReusable 설정:

  • 필터 인스턴스가 원래 생성된 요청 범위 밖에서 재사용될 가능성이 있음을 암시하는 것입니다. ASP.NET Core 런타임은 단일 필터 인스턴스가 생성되도록 보장하지 않습니다.

  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

다음 예제는 TypeFilterAttribute를 사용하여 형식에 인수를 전달하는 방법을 보여줍니다.

[TypeFilter(typeof(LogConstantFilter),
    Arguments = new object[] { "Method 'Hi' called" })]
public IActionResult Hi(string name)
{
    return Content($"Hi {name}");
}

권한 부여 필터

권한 부여 필터는:

  • 필터 파이프라인에서 첫 번째로 실행되는 필터입니다.
  • 작업 메서드에 대한 액세스를 제어합니다.
  • before 메서드는 있지만 after 메서드는 없습니다.

사용자 지정 권한 부여 필터는 사용자 지정 권한 부여 프레임워크를 필요로 합니다. 사용자 지정 필터를 작성하는 대신 권한 부여 정책을 구성하거나 사용자 지정 권한 부여 정책을 작성하는 것이 좋습니다. 기본 제공 권한 부여 필터는:

  • 권한 부여 시스템을 호출합니다.
  • 요청을 승인하지 않습니다.

권한 부여 필터 내에서 예외를 던지지 마세요.

  • 해당 예외는 처리되지 않습니다.
  • 예외 필터가 해당 예외를 처리하지 않습니다.

권한 부여 필터에서 예외가 발생할 경우 챌린지 발행을 고려하세요.

권한 부여에 대해 자세히 알아봅니다.

리소스 필터

리소스 필터는:

리소스 필터는 파이프라인의 대부분을 단락시켜야 하는 경우에 유용합니다. 예를 들어 캐싱 필터는 캐시 적중 시 파이프라인의 나머지 부분을 막을 수 있습니다.

리소스 필터 예제:

작업 필터

작업 필터는 Razor Pages에 적용되지 않습니다. Razor Pages는 IPageFilterIAsyncPageFilter를 지원합니다. 자세한 내용은 Razor Pages에 대한 필터 메서드를 참조하세요.

작업 필터는:

다음 코드는 예제 작업 필터를 보여 줍니다.

public class MySampleActionFilter : IActionFilter 
{
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // Do something before the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }

    public void OnActionExecuted(ActionExecutedContext context)
    {
        // Do something after the action executes.
        MyDebug.Write(MethodBase.GetCurrentMethod(), context.HttpContext.Request.Path);
    }
}

ActionExecutingContext는 다음과 같은 속성을 제공합니다.

  • ActionArguments - 작업 메서드에 대한 입력을 읽을 수 있습니다.
  • Controller - 컨트롤러 인스턴스를 조작할 수 있습니다.
  • Result - Result 설정은 작업 메서드 및 후속 작업 필터의 실행을 단락시킵니다.

작업 메서드에서 예외를 던지면:

  • 후속 필터의 실행을 막습니다.
  • Result 설정과 달리 성공적인 결과 대신 실패로 처리됩니다.

ActionExecutedContextControllerResult에 더하여 다음과 같은 속성을 제공합니다.

  • Canceled - 작업 실행이 다른 필터에 의해 단락된 경우 true입니다.

  • Exception - 작업 또는 이전에 실행된 작업 필터에서 예외가 던져진 경우 null이 아닙니다. 이 속성을 null로 설정하면:

    • 예외를 효과적으로 처리합니다.
    • Result는 작업 메서드에서 반환되는 것처럼 실행됩니다.

IAsyncActionFilter의 경우 ActionExecutionDelegate에 대한 호출은:

  • 후속 작업 필터 및 작업 메서드를 실행합니다.
  • ActionExecutedContext을(를) 반환합니다.

단락시키려면 Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext.Result을 결과 인스턴스에 할당하고 next(ActionExecutionDelegate)를 호출하지 않아야 합니다.

프레임워크는 서브클래싱 할 수 있는 추상 ActionFilterAttribute를 제공합니다.

OnActionExecuting 작업 필터는 다음에 사용할 수 있습니다.

  • 모델 상태의 유효성을 검사합니다.
  • 상태가 유효하지 않은 경우 오류를 반환합니다.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }

참고 항목

[ApiController] 특성으로 주석이 추가된 컨트롤러는 모델 상태의 유효성을 자동으로 검사하고 400 응답을 반환합니다. 자세한 정보는 자동 HTTP 400 응답을 참조하세요. OnActionExecuted 메서드는 작업 메서드 이후에 실행됩니다.

  • 그리고 Result 속성을 통해 작업 결과를 확인하고 조작할 수 있습니다.

  • Canceled는 작업 실행이 다른 필터에 의해 단락된 경우 true로 설정됩니다.

  • Exception은 작업 또는 후속 작업 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. Exception을 Null로 설정:

    • 예외를 효과적으로 처리합니다.
    • ActionExecutedContext.Result은 작업 메서드에서 정상적으로 반환되는 것처럼 실행됩니다.
public class ValidateModelAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext 
                                           context)
    {
        if (!context.ModelState.IsValid)
        {
            context.Result = new BadRequestObjectResult(
                                                context.ModelState);
        }
    }


    public override void OnActionExecuted(ActionExecutedContext 
                                          context)
    {
        var result = context.Result;
        // Do something with Result.
        if (context.Canceled == true)
        {
            // Action execution was short-circuited by another filter.
        }

        if(context.Exception != null)
        {
            // Exception thrown by action or action filter.
            // Set to null to handle the exception.
            context.Exception = null;
        }
        base.OnActionExecuted(context);
    }
}

예외 필터

예외 필터:

다음 예제 예외 필터는 사용자 지정 오류 보기를 사용하여 앱을 개발 중인 경우에 발생하는 예외에 대한 세부 정보를 표시합니다.

public class CustomExceptionFilter : IExceptionFilter
{
    private readonly IWebHostEnvironment _hostingEnvironment;
    private readonly IModelMetadataProvider _modelMetadataProvider;

    public CustomExceptionFilter(
        IWebHostEnvironment hostingEnvironment,
        IModelMetadataProvider modelMetadataProvider)
    {
        _hostingEnvironment = hostingEnvironment;
        _modelMetadataProvider = modelMetadataProvider;
    }

    public void OnException(ExceptionContext context)
    {
        if (!_hostingEnvironment.IsDevelopment())
        {
            return;
        }
        var result = new ViewResult {ViewName = "CustomError"};
        result.ViewData = new ViewDataDictionary(_modelMetadataProvider,
                                                    context.ModelState);
        result.ViewData.Add("Exception", context.Exception);
        // TODO: Pass additional detailed data via ViewData
        context.Result = result;
    }
}

다음 코드에서는 예외 필터를 테스트합니다.

[TypeFilter(typeof(CustomExceptionFilter))]
public class FailingController : Controller
{
    [AddHeader("Failing Controller", 
               "Won't appear when exception is handled")]
    public IActionResult Index()
    {
        throw new Exception("Testing custom exception filter.");
    }
}

예외 필터:

  • before 및 after 이벤트가 없습니다.
  • OnException 또는 OnExceptionAsync를 구현합니다.
  • Razor Page 또는 컨트롤러 생성,모델 바인딩, 작업 필터 또는 작업 메서드에서 발생하는 처리되지 않은 예외를 처리합니다.
  • 리소스 필터, 결과 필터 또는 MVC 결과 실행에서 발생하는 예외를 잡지 않습니다.

예외를 처리하려면 ExceptionHandled 속성을 true로 설정하거나 Result 속성을 할당합니다. 그러면 예외가 전파되지 않습니다. 예외 필터는 예외를 “성공”으로 변환할 수 없습니다. 이는 작업 필터에서만 가능합니다.

예외 필터:

  • 작업 내에서 발생하는 예외를 잡는 데 좋습니다.
  • 오류 처리 미들웨어만큼 유연하지 않습니다.

예외 처리의 경우 미들웨어를 선호합니다. 어떤 작업 메서드가 호출되는지에 따라 오류 처리 방식이 ‘다른’ 경우에만 예외 필터를 사용합니다. 예를 들어 앱에는 API 엔드포인트 및 보기/HTML 모두에 대한 작업 메서드가 있을 수 있습니다. API 엔드포인트는 JSON으로 오류 정보를 반환할 수 있습니다. 반면 보기 기반 작업은 HTML로 오류 페이지를 반환할 수 있습니다.

결과 필터

결과 필터는:

IResultFilter 및 IAsyncResultFilter

다음 코드는 HTTP 헤더를 추가하는 결과 필터를 보여 줍니다.

public class AddHeaderResultServiceFilter : IResultFilter
{
    private ILogger _logger;
    public AddHeaderResultServiceFilter(ILoggerFactory loggerFactory)
    {
        _logger = loggerFactory.CreateLogger<AddHeaderResultServiceFilter>();
    }

    public void OnResultExecuting(ResultExecutingContext context)
    {
        var headerName = "OnResultExecuting";
        context.HttpContext.Response.Headers.Add(
            headerName, new string[] { "ResultExecutingSuccessfully" });
        _logger.LogInformation("Header added: {HeaderName}", headerName);
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
        // Can't add to headers here because response has started.
        _logger.LogInformation("AddHeaderResultServiceFilter.OnResultExecuted");
    }
}

실행되는 결과의 종류는 작업에 따라 다릅니다. 보기를 반환하는 작업에는 실행 중인 ViewResult의 일부로 모든 Razor 프로세스가 포함됩니다. API 메서드는 실행 결과의 일부로 일부 serialization을 수행할 수 있습니다. 작업 결과에 대해 자세히 알아보세요.

결과 필터는 작업 또는 작업 필터가 작업 결과를 생성하는 경우에만 실행됩니다. 다음 경우에는 결과 필터가 실행되지 않습니다.

  • 권한 부여 필터 또는 리소스 필터가 파이프라인을 단락시킬 경우.
  • 예외 필터는 작업 결과를 생성하여 예외를 처리합니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuting 메서드는 Microsoft.AspNetCore.Mvc.Filters.ResultExecutingContext.Canceltrue로 설정하여 작업 결과 및 후속 결과 필터를 단락시킬 수 있습니다. 단락시킬 경우 빈 응답을 생성하지 않도록 응답 개체에 작성하세요. IResultFilter.OnResultExecuting에서 예외 throw:

  • 작업 결과 및 후속 필터의 실행을 막습니다.
  • 성공적인 결과 대신 실패로 처리됩니다.

Microsoft.AspNetCore.Mvc.Filters.IResultFilter.OnResultExecuted 메서드가 실행될 때에는 응답이 이미 클라이언트에 전송되었을 수 있습니다. 이미 클라이언트에 전송된 응답은 변경할 수 없습니다.

ResultExecutedContext.Canceled는 작업 결과 실행이 다른 필터에 의해 단락(short-circuit) 처리된 경우 true로 설정됩니다.

ResultExecutedContext.Exception은 작업 결과 또는 후속 결과 필터에서 예외가 던져진 경우 null이 아닌 값으로 설정됩니다. Exception을 효과적으로 null로 설정하면 예외를 '처리'하고 예외가 파이프라인의 뒷부분에서 다시 throw되지 않습니다. 결과 필터에서 예외를 처리하는 경우 데이터를 응답에 쓸 수 있는 신뢰할 수 있는 방법이 없습니다. 작업 결과가 예외를 던질 때 헤더가 클라이언트에 플러시된 경우 오류 코드를 전송하기 위한 신뢰할 수 있는 메커니즘이 없습니다.

IAsyncResultFilter의 경우 ResultExecutionDelegateawait next 호출은 후속 결과 필터 및 작업 결과를 실행합니다. 단락(short-circuit) 처리하려면 ResultExecutingContext.Canceltrue로 설정하고 ResultExecutionDelegate를 호출하지 않습니다.

public class MyAsyncResponseFilter : IAsyncResultFilter
{
    public async Task OnResultExecutionAsync(ResultExecutingContext context,
                                             ResultExecutionDelegate next)
    {
        if (!(context.Result is EmptyResult))
        {
            await next();
        }
        else
        {
            context.Cancel = true;
        }

    }
}

프레임워크는 서브클래싱 할 수 있는 추상 ResultFilterAttribute를 제공합니다. 이전에 표시된 AddHeaderAttribute 클래스는 결과 필터 특성의 예제입니다.

IAlwaysRunResultFilter 및 IAsyncAlwaysRunResultFilter

IAlwaysRunResultFilterIAsyncAlwaysRunResultFilter 인터페이스는 모든 작업 결과에 대해 실행되는 IResultFilter 구현을 선언합니다. 여기에는 다음에 의해 생성되는 작업 결과가 포함됩니다.

  • 단락하는 권한 부여 필터 및 리소스 필터
  • 예외 필터

예를 들어 다음 필터는 항상 실행되어 콘텐츠 협상이 실패할 경우 작업 결과(ObjectResult)를 422 Unprocessable Entity 상태 코드로 설정합니다.

public class UnprocessableResultFilter : Attribute, IAlwaysRunResultFilter
{
    public void OnResultExecuting(ResultExecutingContext context)
    {
        if (context.Result is StatusCodeResult statusCodeResult &&
            statusCodeResult.StatusCode == (int) HttpStatusCode.UnsupportedMediaType)
        {
            context.Result = new ObjectResult("Can't process this!")
            {
                StatusCode = (int) HttpStatusCode.UnsupportedMediaType,
            };
        }
    }

    public void OnResultExecuted(ResultExecutedContext context)
    {
    }
}

IFilterFactory

IFilterFactoryIFilterMetadata를 구현합니다. 따라서 필터 파이프라인 어디에서나 IFilterFactory 인스턴스를 IFilterMetadata 인스턴스로 사용할 수 있습니다. 런타임이 필터를 호출하려고 준비할 때 IFilterFactory로 캐스팅을 시도합니다. 해당 캐스팅에 성공하면 CreateInstance 메서드를 호출하여 호출되는 IFilterMetadata 인스턴스를 만듭니다. 앱이 시작될 때 정확한 필터 파이프라인을 명시적으로 설정할 필요가 없으므로 유연한 디자인을 제공합니다.

IFilterFactory.IsReusable:

  • 팩터리에서 만든 필터 인스턴스가 생성된 요청 범위 외부에서 재사용될 수 있다는 힌트입니다.
  • 싱글톤 이외의 수명을 가진 서비스에 의존하는 필터와 함께 사용하지 않아야 합니다.

ASP.NET Core 런타임은 다음을 보장하지 않습니다.

  • 필터의 단일 인스턴스 생성.
  • 나중에 DI 컨테이너에서 필터가 다시 요청되지 않음.

Warning

필터의 원본이 명확하고, 필터가 상태 비저장이며, 필터가 여러 HTTP 요청에서 안전하게 사용할 수 있는 경우에만 true를 반환하도록 IFilterFactory.IsReusable을 구성합니다. 예를 들어 IFilterFactory.IsReusabletrue를 반환하는 경우 범위가 지정되거나 일시적으로 등록된 DI에서 필터를 반환하지 마세요. 필터를 만드는 다른 방법으로 사용자 지정 특성 구현을 사용하여 IFilterFactory를 구현할 수 있습니다.

public class AddHeaderWithFactoryAttribute : Attribute, IFilterFactory
{
    // Implement IFilterFactory
    public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
    {
        return new InternalAddHeaderFilter();
    }

    private class InternalAddHeaderFilter : IResultFilter
    {
        public void OnResultExecuting(ResultExecutingContext context)
        {
            context.HttpContext.Response.Headers.Add(
                "Internal", new string[] { "My header" });
        }

        public void OnResultExecuted(ResultExecutedContext context)
        {
        }
    }

    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
}

필터는 다음 코드에서 적용됩니다.

[AddHeader("Author", "Rick Anderson")]
public class SampleController : Controller
{
    public IActionResult Index()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }

    [ServiceFilter(typeof(MyActionFilterAttribute))]
    public IActionResult Index2()
    {
        return Content("Header values by configuration.");
    }

    [ShortCircuitingResourceFilter]
    public IActionResult SomeResource()
    {
        return Content("Successful access to resource - header is set.");
    }

    [AddHeaderWithFactory]
    public IActionResult HeaderWithFactory()
    {
        return Content("Examine the headers using the F12 developer tools.");
    }
}

샘플 다운로드 샘플를 실행하여 이전 코드를 테스트할 수 있습니다.

  • F12 개발자 도구를 호출합니다.
  • https://localhost:5001/Sample/HeaderWithFactory으로 이동합니다.

F12 개발자 도구는 샘플 코드에 의해 추가된 다음 응답 헤더를 표시합니다.

  • author:Rick Anderson
  • globaladdheader:Result filter added to MvcOptions.Filters
  • internalMy header:

이전 코드는 internal:My header 응답 헤더를 만듭니다.

특성에서 구현된 IFilterFactory

IFilterFactory를 구현하는 필터는 다음과 같은 필터에 유용합니다.

  • 매개 변수 전달을 필요로 하지 않는 필터.
  • DI에 의해 채워져야 할 생성자 종속성이 있는 필터.

TypeFilterAttributeIFilterFactory를 구현합니다. IFilterFactoryIFilterMetadata 인스턴스를 만들기 위해 CreateInstance 메서드를 노출합니다. CreateInstance는 서비스 컨테이너(DI)에서 지정된 형식을 로드합니다.

public class SampleActionFilterAttribute : TypeFilterAttribute
{
    public SampleActionFilterAttribute()
                         :base(typeof(SampleActionFilterImpl))
    { 
    }

    private class SampleActionFilterImpl : IActionFilter
    {
        private readonly ILogger _logger;
        public SampleActionFilterImpl(ILoggerFactory loggerFactory)
        {
            _logger = loggerFactory.CreateLogger<SampleActionFilterAttribute>();
        }

        public void OnActionExecuting(ActionExecutingContext context)
        {
           _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuting");
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            _logger.LogInformation("SampleActionFilterAttribute.OnActionExecuted");
        }
    }
}

다음 코드는 [SampleActionFilter]을 적용하는 세 가지 방법을 보여 줍니다.

[SampleActionFilter]
public IActionResult FilterTest()
{
    return Content("From FilterTest");
}

[TypeFilter(typeof(SampleActionFilterAttribute))]
public IActionResult TypeFilterTest()
{
    return Content("From TypeFilterTest");
}

// ServiceFilter must be registered in ConfigureServices or
// System.InvalidOperationException: No service for type '<filter>'
// has been registered. Is thrown.
[ServiceFilter(typeof(SampleActionFilterAttribute))]
public IActionResult ServiceFilterTest()
{
    return Content("From ServiceFilterTest");
}

이전 코드에서 [SampleActionFilter]로 메서드를 장식하는 것이 SampleActionFilter을 적용하는 데 선호되는 방법입니다.

필터 파이프라인에서 미들웨어 사용

리소스 필터는 파이프라인의 뒷부분에 제공되는 모든 실행을 둘러싼다는 점에서 미들웨어처럼 작동합니다. 하지만 필터는 런타임의 일부라는 점에서 미들웨어와 다릅니다. 즉, 컨텍스트 및 구문에 액세스할 수 있습니다.

미들웨어를 필터로 사용하려면 필터 파이프라인에 삽입할 미들웨어를 지정하는 Configure 메서드를 포함한 형식을 만듭니다. 다음 예제는 지역화 미들웨어를 사용하여 요청에 대한 현재 문화권을 설정합니다.

public class LocalizationPipeline
{
    public void Configure(IApplicationBuilder applicationBuilder)
    {
        var supportedCultures = new[]
        {
            new CultureInfo("en-US"),
            new CultureInfo("fr")
        };

        var options = new RequestLocalizationOptions
        {
            DefaultRequestCulture = new RequestCulture(
                                       culture: "en-US", 
                                       uiCulture: "en-US"),
            SupportedCultures = supportedCultures,
            SupportedUICultures = supportedCultures
        };
        options.RequestCultureProviders = new[] 
            { new RouteDataRequestCultureProvider() {
                Options = options } };

        applicationBuilder.UseRequestLocalization(options);
    }
}

MiddlewareFilterAttribute를 사용하여 미들웨어를 실행합니다.

[Route("{culture}/[controller]/[action]")]
[MiddlewareFilter(typeof(LocalizationPipeline))]
public IActionResult CultureFromRouteData()
{
    return Content(
          $"CurrentCulture:{CultureInfo.CurrentCulture.Name},"
        + $"CurrentUICulture:{CultureInfo.CurrentUICulture.Name}");
}

미들웨어 필터는 모델 바인딩 이전 및 나머지 파이프라인 이후에 리소스 필터와 동일한 필터 파이프라인 단계에서 실행됩니다.

스레드로부터의 안전성

Type 대신 필터의 인스턴스Add에 전달하는 경우 필터는 싱글톤이며 스레드로부터 안전하지 않습니다.

다음 작업