ASP.NET Core 미들웨어 기본 사항ASP.NET Core Middleware Fundamentals

<a name=fundamentals-middleware>

Rick AndersonSteve SmithBy Rick Anderson and Steve Smith

보거나 다운로드 샘플 코드 (다운로드 하는 방법을)View or download sample code (how to download)

미들웨어는 무엇입니까What is middleware

미들웨어는 소프트웨어 요청 및 응답을 처리 하는 응용 프로그램 파이프라인에 넣을입니다.Middleware is software that is assembled into an application pipeline to handle requests and responses. 각 구성 요소입니다.Each component:

  • 다음 구성 요소는 파이프라인에 요청을 통과 것인지를 선택 합니다.Chooses whether to pass the request to the next component in the pipeline.
  • 파이프라인의 다음 구성 요소로 호출 된 후 전과 작업을 수행할 수 있습니다.Can perform work before and after the next component in the pipeline is invoked.

요청 대리자 요청 파이프라인을 구축 하는 데 사용 됩니다.Request delegates are used to build the request pipeline. 요청 대리자는 각 HTTP 요청을 처리합니다.The request delegates handle each HTTP request.

대리자를 사용 하 여 구성 된 요청 실행, 지도, 및 사용 확장 메서드입니다.Request delegates are configured using Run, Map, and Use extension methods. 개별 요청 대리자 (인라인 미들웨어 라고 함)는 무명 메서드의 인라인으로 지정된 될 수 또는 다시 사용할 수 있는 클래스에서 정의할 수 있습니다.An individual request delegate can be specified in-line as an anonymous method (called in-line middleware), or it can be defined in a reusable class. 이러한 다시 사용할 수 있는 클래스와 인라인 무명 메서드는 미들웨어, 또는 미들웨어 구성 요소합니다.These reusable classes and in-line anonymous methods are middleware, or middleware components. 요청 파이프라인의 각 미들웨어 구성 요소는 파이프라인의 다음 구성 요소를 호출 하거나 해당 하는 경우 체인 단락 (short-circuiting) 하는 일을 담당 합니다.Each middleware component in the request pipeline is responsible for invoking the next component in the pipeline, or short-circuiting the chain if appropriate.

미들웨어 마이그레이션 HTTP 모듈 ASP.NET Core의 요청 파이프라인 및 이전 버전 간의 차이 설명 하 고 많은 미들웨어 샘플을 제공 합니다.Migrating HTTP Modules to Middleware explains the difference between request pipelines in ASP.NET Core and the previous versions and provides more middleware samples.

IApplicationBuilder 미들웨어 파이프라인 만들기Creating a middleware pipeline with IApplicationBuilder

ASP.NET Core 요청 파이프라인의 요청 대리자, 즉,이 다이어그램 (실행 다음과 검은색 화살표의 스레드)와 같이 호출 시퀀스로 구성 됩니다.The ASP.NET Core request pipeline consists of a sequence of request delegates, called one after the other, as this diagram shows (the thread of execution follows the black arrows):

요청 처리 패턴 도착, 세 가지 middlewares 및 응용 프로그램이 응답을 통해 처리 요청을 표시 합니다.

각 대리자 이전 및 다음 대리자 이후 작업을 수행할 수 있습니다.Each delegate can perform operations before and after the next delegate. 또한 대리자 요청 파이프라인을 단락 (short-circuiting) 호출 하는 다음 대리자에는 요청을 통과 하지 결정할 수 있습니다.A delegate can also decide to not pass a request to the next delegate, which is called short-circuiting the request pipeline. 불필요 한 작업을 방지 하기 때문에 단락 (short-circuiting)은 바람직한 경우가 많습니다.Short-circuiting is often desirable because it avoids unnecessary work. 예를 들어 정적 파일 미들웨어 정적 파일에 대 한 요청을 반환 하 고 나머지 파이프라인 단락 (short-circuit) 수 있습니다.For example, the static file middleware can return a request for a static file and short-circuit the rest of the pipeline. 예외 처리 대리자 파이프라인의 이후 단계에서 발생 하는 예외를 catch 할 수 있으므로 파이프라인, 초기에 호출 해야 합니다.Exception-handling delegates need to be called early in the pipeline, so they can catch exceptions that occur in later stages of the pipeline.

가장 간단한 가능한 ASP.NET Core 응용 프로그램은 모든 요청을 처리 하는 단일 요청 대리자를 설정 합니다.The simplest possible ASP.NET Core app sets up a single request delegate that handles all requests. 이 경우 실제 요청 파이프라인에 포함 되지 않습니다.This case doesn't include an actual request pipeline. 대신, 단일 익명 함수는 모든 HTTP 요청에 대 한 응답으로 호출 됩니다.Instead, a single anonymous function is called in response to every HTTP request.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello, World!");
        });
    }
}

첫 번째 응용 프로그램입니다. 실행 대리자 파이프라인을 종료 합니다.The first app.Run delegate terminates the pipeline.

연결할 수 있습니다. 여러 요청 대리자와 함께 응용 프로그램입니다. 사용 하 여합니다.You can chain multiple request delegates together with app.Use. next 매개 변수는 파이프라인의 다음 대리자를 나타냅니다.The next parameter represents the next delegate in the pipeline. (하 여 파이프라인을 단락 수 한다는 기억 하지 호출는 다음 매개 변수.) 일반적으로 작업을 수행할 수는 다음 대리자 앞뒤 모두이 예제에서 보여 주듯이:(Remember that you can short-circuit the pipeline by not calling the next parameter.) You can typically perform actions both before and after the next delegate, as this example demonstrates:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use(async (context, next) =>
        {
            // Do work that doesn't write to the Response.
            await next.Invoke();
            // Do logging or other work that doesn't write to the Response.
        });

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from 2nd delegate.");
        });
    }
}
경고

호출 하지 마십시오 next.Invoke 클라이언트에 응답을 보낸 후 합니다.Do not call next.Invoke after the response has been sent to the client. 변경 내용 HttpResponse 응답이 시작 된 후 예외가 throw 됩니다.Changes to HttpResponse after the response has started will throw an exception. 예를 들어 헤더, 상태 코드 등을 설정 하는 등의 변경에는 예외가 throw 됩니다.For example, changes such as setting headers, status code, etc, will throw an exception. 호출한 후 응답 본문에 쓰기 next:Writing to the response body after calling next:

  • 프로토콜 위반이 발생할 수 있습니다.May cause a protocol violation. 예를 들어 쓰기는 언급 된 것 보다 많은 content-length합니다.For example, writing more than the stated content-length.
  • 본문 형식을 손상 될 수 있습니다.May corrupt the body format. 예를 들어 HTML 바닥글 CSS 파일에 기록 됩니다.For example, writing an HTML footer to a CSS file.

HttpResponse.HasStarted 경우 헤더를 보낸 및/또는 본문에 기록 되었는지 나타내는에 유용한 정보를 제공 합니다.HttpResponse.HasStarted is a useful hint to indicate if headers have been sent and/or the body has been written to.

정렬Ordering

미들웨어 구성 요소에 추가 되는 순서는 Configure 요청에서 호출 되는 순서와 역순은 응답에 대 한 메서드를 정의 합니다.The order that middleware components are added in the Configure method defines the order in which they are invoked on requests, and the reverse order for the response. 이 순서 지정 하는 것은 보안, 성능 및 기능에 대 한 중요 합니다.This ordering is critical for security, performance, and functionality.

Configure 메서드 (아래 참조) 다음 미들웨어 구성 요소를 추가 합니다.The Configure method (shown below) adds the following middleware components:

  1. 예외/오류 처리Exception/error handling
  2. 정적 파일 서버Static file server
  3. 인증Authentication
  4. MVCMVC
public void Configure(IApplicationBuilder app)
{
    app.UseExceptionHandler("/Home/Error"); // Call first to catch exceptions
                                            // thrown in the following middleware.

    app.UseStaticFiles();                   // Return static files and end pipeline.

    app.UseAuthentication();               // Authenticate before you access
                                           // secure resources.

    app.UseMvcWithDefaultRoute();          // Add MVC to the request pipeline.
}

위의 코드에서 UseExceptionHandler 파이프라인에 추가 하는 첫 번째 미들웨어 구성 요소는-따라서 대 한 후속 호출에서 발생 하는 모든 예외를 catch 합니다.In the code above, UseExceptionHandler is the first middleware component added to the pipeline—therefore, it catches any exceptions that occur in later calls.

요청을 처리 하 고 나머지 구성 요소를 통과 하지 않고 단락 (short-circuit) 수 있도록 정적 파일 미들웨어 파이프라인 초기에 호출 됩니다.The static file middleware is called early in the pipeline so it can handle requests and short-circuit without going through the remaining components. 정적 파일 미들웨어 제공 없는 권한 부여 확인 합니다.The static file middleware provides no authorization checks. 모든 파일에서 제공 비롯 wwwroot, 공개적으로 사용할 수 있습니다.Any files served by it, including those under wwwroot, are publicly available. 참조 정적 파일 작업 정적 파일을 보호 하는 방법에 대 한 합니다.See Working with static files for an approach to secure static files.

요청이 정적 파일 미들웨어에서 처리 되지 않으면 Identity 미들웨어 전달 됩니다 (app.UseAuthentication), 인증을 수행 하는 합니다.If the request is not handled by the static file middleware, it's passed on to the Identity middleware (app.UseAuthentication), which performs authentication. Identity 인증 되지 않은 요청을 단락 하지 않습니다.Identity does not short-circuit unauthenticated requests. Identity 요청을 인증 하는 있지만 권한 부여 (및 거부) MVC가 특정 Razor 페이지 또는 컨트롤러 및 작업을 선택한 후에 발생 합니다.Although Identity authenticates requests, authorization (and rejection) occurs only after MVC selects a specific Razor Page or controller and action.

다음 예제는 정적 파일에 대 한 요청 응답 압축 미들웨어 하기 전에 정적 파일 미들웨어에서 처리 되는 곳 순서 미들웨어입니다.The following example demonstrates a middleware ordering where requests for static files are handled by the static file middleware before the response compression middleware. 정적 파일 미들웨어의이 순서는 압축 되지 않습니다.Static files are not compressed with this ordering of the middleware. MVC 응답을 UseMvcWithDefaultRoute 압축할 수 있습니다.The MVC responses from UseMvcWithDefaultRoute can be compressed.

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles();         // Static files not compressed
                                  // by middleware.
    app.UseResponseCompression();
    app.UseMvcWithDefaultRoute();
}

<a name=middleware-run-map-use>

사용 하 여, 실행 및 매핑Use, Run, and Map

사용 하 여 HTTP 파이프라인을 구성 Use, Run, 및 Map합니다.You configure the HTTP pipeline using Use, Run, and Map. Use 메서드 수 단락 (short-circuit) 파이프라인 (즉, 호출 하지 않는 경우는 next 요청 대리자).The Use method can short-circuit the pipeline (that is, if it does not call a next request delegate). Run규칙은 일부 미들웨어 구성 요소 노출 될 수 있습니다 및 Run[Middleware] 파이프라인의 끝에 실행 되는 메서드.Run is a convention, and some middleware components may expose Run[Middleware] methods that run at the end of the pipeline.

Map*확장이는 파이프라인 분기에 규칙으로 사용 됩니다.Map* extensions are used as a convention for branching the pipeline. 지도 지정된 된 요청 경로 일치를 기반으로 요청 파이프라인 분기 합니다.Map branches the request pipeline based on matches of the given request path. 요청 경로 지정 된 경로로 시작 되 면 분기가 실행 되 고 있습니다.If the request path starts with the given path, the branch is executed.

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

다음 표에서 요청 및 응답을 보여 줍니다. http://localhost:1234 앞의 코드를 사용 하 여:The following table shows the requests and responses from http://localhost:1234 using the previous code:

요청Request 응답Response
localhost:1234localhost:1234 맵 비 대리자에서 번호입니다.Hello from non-Map delegate.
localhost:1234 / map1localhost:1234/map1 맵 테스트 1Map Test 1
localhost:1234 / map2localhost:1234/map2 맵 테스트 2Map Test 2
localhost:1234 / map3localhost:1234/map3 맵 비 대리자에서 번호입니다.Hello from non-Map delegate.

Map 는 사용 하는 일치 하는 경로 세그먼트에서 제거 됩니다 HttpRequest.Path 에 추가 되 고 HttpRequest.PathBase 각 요청에 대 한 합니다.When Map is used, the matched path segment(s) are removed from HttpRequest.Path and appended to HttpRequest.PathBase for each request.

MapWhen 지정된 된 조건자의 결과 기반으로 요청 파이프라인 분기 합니다.MapWhen branches the request pipeline based on the result of the given predicate. 형식 조건자를 임의의 조건자 Func<HttpContext, bool> 요청 파이프라인의 새 분기에 매핑하기 위해 사용할 수 있습니다.Any predicate of type Func<HttpContext, bool> can be used to map requests to a new branch of the pipeline. 쿼리 문자열 변수의 현재 상태를 찾는 데 사용 되는 조건자 다음 예에서 branch:In the following example, a predicate is used to detect the presence of a query string variable branch:

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}

다음 표에서 요청 및 응답을 보여 줍니다. http://localhost:1234 앞의 코드를 사용 하 여:The following table shows the requests and responses from http://localhost:1234 using the previous code:

요청Request 응답Response
localhost:1234localhost:1234 맵 비 대리자에서 번호입니다.Hello from non-Map delegate.
localhost:1234 /? 분기 마스터 =localhost:1234/?branch=master 분기 사용 되는 마스터 =Branch used = master

Map예를 들어 중첩 지원:Map supports nesting, for example:

app.Map("/level1", level1App => {
       level1App.Map("/level2a", level2AApp => {
           // "/level1/level2a"
           //...
       });
       level1App.Map("/level2b", level2BApp => {
           // "/level1/level2b"
           //...
       });
   });

Map또한 일치 시킬 수 여러 세그먼트를 한 번에 예:Map can also match multiple segments at once, for example:

app.Map("/level1/level2", HandleMultiSeg);

기본 제공 미들웨어Built-in middleware

ASP.NET Core 다음 미들웨어 구성 요소와 함께 제공 합니다.ASP.NET Core ships with the following middleware components:

미들웨어Middleware 설명Description
인증Authentication 인증 지원을 제공합니다.Provides authentication support.
CORSCORS 크로스-원본 자원 공유를 구성합니다.Configures Cross-Origin Resource Sharing.
응답 캐싱Response Caching 응답을 캐시에 대 한 지원을 제공 합니다.Provides support for caching responses.
응답 압축Response Compression 응답을 압축 하는 것에 대 한 지원을 제공 합니다.Provides support for compressing responses.
라우팅Routing 정의 하 고 요청 경로 제한 합니다.Defines and constrains request routes.
세션Session 사용자 세션을 관리 하기 위한 지원을 제공 합니다.Provides support for managing user sessions.
정적 파일Static Files 정적 파일 및 디렉터리 검색을 처리 하기 위한 지원을 제공 합니다.Provides support for serving static files and directory browsing.
URL 재작성 미들웨어URL Rewriting Middleware Url 다시 쓰기 및 요청 리디렉션에 대 한 지원을 제공 합니다.Provides support for rewriting URLs and redirecting requests.

<a name=middleware-writing-middleware>

쓰기 미들웨어Writing middleware

일반적으로 미들웨어 클래스에 캡슐화 하 고 확장 메서드로 노출 됩니다.Middleware is generally encapsulated in a class and exposed with an extension method. 쿼리 문자열에서 현재 요청에 대 한 문화권을 설정 하는 다음 미들웨어를 고려 합니다.Consider the following middleware, which sets the culture for the current request from the query string:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.Use((context, next) =>
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;
            }

            // Call the next delegate/middleware in the pipeline
            return next();
        });

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });

    }
}

참고: 위의 샘플 코드 미들웨어 구성 요소 만들기를 보여 주기 위해 사용 됩니다.Note: The sample code above is used to demonstrate creating a middleware component. 참조 전역화 및 지역화 ASP.NET Core 기본 제공 지역화 지원에 대 한 합니다.See Globalization and localization for ASP.NET Core's built-in localization support.

예를 들어 culture에 전달 하 여 미들웨어를 테스트할 수 있습니다 http://localhost:7997/?culture=no합니다.You can test the middleware by passing in the culture, for example http://localhost:7997/?culture=no.

다음 코드는 미들웨어 대리자 클래스에 이동합니다.The following code moves the middleware delegate to a class:

using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace Culture
{
    public class RequestCultureMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestCultureMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public Task Invoke(HttpContext context)
        {
            var cultureQuery = context.Request.Query["culture"];
            if (!string.IsNullOrWhiteSpace(cultureQuery))
            {
                var culture = new CultureInfo(cultureQuery);

                CultureInfo.CurrentCulture = culture;
                CultureInfo.CurrentUICulture = culture;

            }

            // Call the next delegate/middleware in the pipeline
            return this._next(context);
        }
    }
}

다음 확장 메서드는 미들웨어를 통해 노출 IApplicationBuilder:The following extension method exposes the middleware through IApplicationBuilder:

using Microsoft.AspNetCore.Builder;

namespace Culture
{
    public static class RequestCultureMiddlewareExtensions
    {
        public static IApplicationBuilder UseRequestCulture(
            this IApplicationBuilder builder)
        {
            return builder.UseMiddleware<RequestCultureMiddleware>();
        }
    }
}

다음 코드에서 미들웨어를 호출 Configure:The following code calls the middleware from Configure:

public class Startup
{
    public void Configure(IApplicationBuilder app)
    {
        app.UseRequestCulture();

        app.Run(async (context) =>
        {
            await context.Response.WriteAsync(
                $"Hello {CultureInfo.CurrentCulture.DisplayName}");
        });

    }
}

미들웨어 따라야는 명시적 종속성 원칙 생성자에서 해당 종속성을 노출 하 여 합니다.Middleware should follow the Explicit Dependencies Principle by exposing its dependencies in its constructor. 미들웨어 당 한 번 생성 응용 프로그램 수명합니다.Middleware is constructed once per application lifetime. 참조 요청별 종속성 경우에는 아래 서비스 요청 내의 미들웨어를 공유 해야 합니다.See Per-request dependencies below if you need to share services with middleware within a request.

미들웨어 구성 요소 생성자 매개 변수를 통해 종속성 주입에서 해당 종속성을 확인할 수 있습니다.Middleware components can resolve their dependencies from dependency injection through constructor parameters. UseMiddleware<T>추가 매개 변수를 직접 적용할 수도.UseMiddleware<T> can also accept additional parameters directly.

요청별 종속성Per-request dependencies

미들웨어 하지 요청별, 응용 프로그램 시작 시 구성 된 때문에 범위 미들웨어 생성자에 의해 사용 되는 수명을 서비스 요청 될 때마다 다른 종속성 주입 종류와 공유 되지 않습니다.Because middleware is constructed at app startup, not per-request, scoped lifetime services used by middleware constructors are not shared with other dependency-injected types during each request. 공유 해야 하는 경우는 범위 미들웨어와 다른 형식 간의 서비스, 이러한 서비스를 추가 Invoke 메서드의 서명입니다.If you must share a scoped service between your middleware and other types, add these services to the Invoke method's signature. Invoke 메서드 종속성 주입으로 채우는 추가 매개 변수를 사용할 수 있습니다.The Invoke method can accept additional parameters that are populated by dependency injection. 예:For example:

public class MyMiddleware
{
    private readonly RequestDelegate _next;

    public MyMiddleware(RequestDelegate next)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext, IMyScopedService svc)
    {
        svc.MyProperty = 1000;
        await _next(httpContext);
    }
}

리소스Resources