다음을 통해 공유


ASP.NET Framework HttpContext를 ASP.NET Core로 마이그레이션

HttpContext는 HTTP 요청 및 응답 정보에 대한 액세스를 제공하는 웹 애플리케이션의 기본 구성 요소입니다. ASP.NET Framework에서 ASP.NET Core로 마이그레이션할 때 HttpContext는 두 프레임워크에 서로 다른 API와 접근 방식이 있기 때문에 고유한 과제를 제시합니다.

HttpContext 마이그레이션이 복잡한 이유

ASP.NET Framework 및 ASP.NET Core에는 기본적으로 다른 HttpContext 구현이 있습니다.

이러한 차이는 변경 없이 HttpContext 코드를 프레임워크에서 Core로 단순히 이동할 수 없다는 것을 의미합니다.

마이그레이션 전략 개요

마이그레이션 중에 HttpContext를 처리하는 두 가지 주요 방법이 있습니다.

  1. 다시 쓰기 완료 - 모든 HttpContext 코드를 다시 작성하여 ASP.NET Core의 네이티브 HttpContext 구현 사용
  2. System.Web 어댑터 - 어댑터를 사용하여 증분 마이그레이션하는 동안 코드 변경 최소화

대부분의 애플리케이션에서 ASP.NET Core의 네이티브 HttpContext로 마이그레이션하면 최상의 성능과 유지 관리가 제공됩니다. 그러나 대규모 애플리케이션 또는 광범위한 HttpContext 사용량이 있는 애플리케이션은 증분 마이그레이션 중에 System.Web 어댑터를 사용하면 도움이 될 수 있습니다.

마이그레이션 접근 방식 선택

HttpContext를 ASP.NET Framework에서 ASP.NET Core로 마이그레이션하는 두 가지 주요 옵션이 있습니다. 마이그레이션 타임라인, 두 애플리케이션을 동시에 실행해야 하는지 여부 및 다시 작성하려는 코드 양에 따라 선택할 수 있습니다.

빠른 의사 결정 가이드

다음 질문에 대답하여 접근 방식을 선택합니다.

  1. 전체 다시 쓰기 또는 증분 마이그레이션을 수행하고 있나요?

  2. 공유 라이브러리에서 광범위한 HttpContext 사용량이 있나요?

마이그레이션 접근 방식 비교

접근법 코드 변경 내용 성능 공유 라이브러리 사용 시기
다시 쓰기 완료 높음 - 모든 HttpContext 코드 다시 쓰기 최상의 업데이트 필요 전체 재작성, 성능이 중요한 앱
System.Web 어댑터 낮음 - 기존 패턴 유지 좋음 기존 코드 사용 증분 마이그레이션, 광범위한 HttpContext 사용

중요한 차이점

HttpContext 수명

어댑터는 요청의 수명이 끝나면 사용할 수 없는 HttpContext에 의해 지원됩니다. HttpContext 따라서 ASP.NET Core에서 실행하는 경우 요청을 지나서도 사용할 수 없지만 ASP.NET Framework에서는 때때로 작동합니다. ObjectDisposedException 요청이 종료된 후 사용되는 경우 throw됩니다.

권장 사항: POCO에 필요한 값을 저장하고 이를 유지합니다.

요청 스레드 처리 고려 사항

경고

ASP.NET Core는 요청에 대한 스레드 선호도를 보장하지 않습니다. 코드에서 HttpContext에 대한 스레드 안전한 액세스가 필요한 경우, 적절한 동기화를 보장해야 합니다.

ASP.NET Framework에서 요청은 스레드 선호도를 가지며 Current 해당 스레드에 있는 경우에만 사용할 수 있습니다. ASP.NET Core에는 이 보장이 없으므로 Current 동일한 비동기 컨텍스트 내에서 사용할 수 있지만 스레드에 대한 보장은 없습니다.

권장 사항: HttpContext에 읽기/쓰기를 하는 경우, 반드시 단일 스레드 방식으로 수행해야 합니다. 을 설정 ISingleThreadedRequestMetadata하여 비동기 컨텍스트에서 요청이 동시에 실행되지 않도록 강제할 수 있습니다. 이는 성능에 영향을 미치며 비동기 액세스를 보장하기 위해 사용량을 리팩터링할 수 없는 경우에만 사용해야 합니다. 다음을 사용하여 컨트롤러에 추가할 수 있는 구현이 있습니다.SingleThreadedRequestAttribute

[SingleThreadedRequest]
public class SomeController : Controller
{
    ...
} 

요청 스트림 버퍼링

기본적으로 들어오는 요청이 항상 검색 가능하거나 완전히 사용할 수 있는 것은 아닙니다. .NET Framework에서 동작을 확인하려면 입력 스트림의 사전 버퍼링을 옵트인할 수 있습니다. 그러면 들어오는 스트림을 완전히 읽고 메모리 또는 디스크에 버퍼링합니다(설정에 따라 다름).

권장 사항: 인터페이스를 구현하는 엔드포인트 메타데이터를 적용하여 사용하도록 설정할 수 있습니다 IPreBufferRequestStreamMetadata . 컨트롤러 또는 메서드에 적용할 수 있는 특성 PreBufferRequestStreamAttribute 으로 사용할 수 있습니다.

모든 MVC 엔드포인트에서 이를 사용하도록 설정하려면 다음과 같이 사용할 수 있는 확장 메서드가 있습니다.

app.MapDefaultControllerRoute()
    .PreBufferRequestStream();

응답 스트림 버퍼링

일부 Response의 API는 Output, End(), Clear(), SuppressContent와 같은 출력 스트림이 버퍼링되어야 합니다.

권장 사항: 보내기 전에 응답을 버퍼링해야 하는 동작 Response 을 지원하려면 엔드포인트가 엔드포인트 메타데이터를 구현하여 IBufferResponseStreamMetadata옵트인해야 합니다.

모든 MVC 엔드포인트에서 이를 사용하도록 설정하려면 다음과 같이 사용할 수 있는 확장 메서드가 있습니다.

app.MapDefaultControllerRoute()
    .BufferResponseStream();

ASP.NET Core HttpContext로의 완전한 재작성 완료

전체 마이그레이션을 수행할 때 이 방법을 선택하고 HttpContext 관련 코드를 다시 작성하여 ASP.NET Core의 네이티브 구현을 사용할 수 있습니다.

ASP.NET Core의 HttpContext는 ASP.NET Framework에 비해 더 모듈화되고 확장 가능한 디자인을 제공합니다. 이 방법은 최상의 성능을 제공하지만 마이그레이션 중에 더 많은 코드 변경이 필요합니다.

개요

HttpContext 은 ASP.NET Core에서 크게 변경되었습니다. HTTP 모듈 또는 처리기를 미들웨어로 마이그레이션할 때 새 HttpContext API를 사용하도록 코드를 업데이트해야 합니다.

ASP.NET Core 미들웨어 Invoke 에서 메서드는 다음과 같은 형식 HttpContext의 매개 변수를 사용합니다.

public async Task Invoke(HttpContext context)

이는 HttpContext ASP.NET Framework 버전과 다르며 요청 및 응답 정보에 액세스하기 위한 다양한 접근 방식이 필요합니다.

속성 이름 번역

이 섹션에서는 가장 일반적으로 사용되는 속성을 ASP.NET Core의 System.Web.HttpContext 해당 Microsoft.AspNetCore.Http.HttpContext 속성으로 변환하는 방법을 보여 있습니다.

HttpContext 속성

HttpRequest 속성

  • HttpRequest.HttpMethodHttpRequest.Method

    string httpMethod = httpContext.Request.Method;
    
  • HttpRequest.QueryStringHttpRequest.QueryString

    IQueryCollection queryParameters = httpContext.Request.Query;
    
    // If no query parameter "key" used, values will have 0 items
    // If single value used for a key (...?key=v1), values will have 1 item ("v1")
    // If key has multiple values (...?key=v1&key=v2), values will have 2 items ("v1" and "v2")
    IList<string> values = queryParameters["key"];
    
    // If no query parameter "key" used, value will be ""
    // If single value used for a key (...?key=v1), value will be "v1"
    // If key has multiple values (...?key=v1&key=v2), value will be "v1,v2"
    string value = queryParameters["key"].ToString();
    
  • HttpRequest.Url / HttpRequest.RawUrl 여러 속성

    // using Microsoft.AspNetCore.Http.Extensions;
    var url = httpContext.Request.GetDisplayUrl();
    

    Request.Scheme, Host, PathBase, Path, QueryString을 사용하십시오

  • HttpRequest.IsSecureConnectionHttpRequest.IsHttps

    var isSecureConnection = httpContext.Request.IsHttps;
    
  • HttpRequest.UserHostAddressConnectionInfo.RemoteIpAddress

    var userHostAddress = httpContext.Connection.RemoteIpAddress?.ToString();
    
  • HttpRequest.CookiesHttpRequest.Cookies

    IRequestCookieCollection cookies = httpContext.Request.Cookies;
    string unknownCookieValue = cookies["unknownCookie"]; // will be null (no exception)
    string knownCookieValue = cookies["cookie1name"];     // will be actual value
    
  • HttpRequest.RequestContextRoutingHttpContextExtensions.GetRouteData

    var routeValue = httpContext.GetRouteValue("key");
    
  • HttpRequest.HeadersHttpRequest.Headers

    // using Microsoft.AspNetCore.Http.Headers;
    // using Microsoft.Net.Http.Headers;
    
    IHeaderDictionary headersDictionary = httpContext.Request.Headers;
    
    // GetTypedHeaders extension method provides strongly typed access to many headers
    var requestHeaders = httpContext.Request.GetTypedHeaders();
    CacheControlHeaderValue cacheControlHeaderValue = requestHeaders.CacheControl;
    
    // For unknown header, unknownheaderValues has zero items and unknownheaderValue is ""
    IList<string> unknownheaderValues = headersDictionary["unknownheader"];
    string unknownheaderValue = headersDictionary["unknownheader"].ToString();
    
    // For known header, knownheaderValues has 1 item and knownheaderValue is the value
    IList<string> knownheaderValues = headersDictionary[HeaderNames.AcceptLanguage];
    string knownheaderValue = headersDictionary[HeaderNames.AcceptLanguage].ToString();
    
  • HttpRequest.UserAgentHttpRequest.Headers

    string userAgent = headersDictionary[HeaderNames.UserAgent].ToString();
    
  • HttpRequest.UrlReferrerHttpRequest.Headers

    string urlReferrer = headersDictionary[HeaderNames.Referer].ToString();
    
  • HttpRequest.ContentTypeHttpRequest.ContentType

    // using Microsoft.Net.Http.Headers;
    
    MediaTypeHeaderValue mediaHeaderValue = requestHeaders.ContentType;
    string contentType = mediaHeaderValue?.MediaType.ToString();   // ex. application/x-www-form-urlencoded
    string contentMainType = mediaHeaderValue?.Type.ToString();    // ex. application
    string contentSubType = mediaHeaderValue?.SubType.ToString();  // ex. x-www-form-urlencoded
    
    System.Text.Encoding requestEncoding = mediaHeaderValue?.Encoding;
    
  • HttpRequest.FormHttpRequest.Form

    if (httpContext.Request.HasFormContentType)
    {
        IFormCollection form;
    
        form = httpContext.Request.Form; // sync
        // Or
        form = await httpContext.Request.ReadFormAsync(); // async
    
        string firstName = form["firstname"];
        string lastName = form["lastname"];
    }
    

    경고: 콘텐츠 형식이 x-www-form-urlencoded 또는 form-data인 경우에만 양식 값을 읽습니다.

  • HttpRequest.InputStreamHttpRequest.Body

    string inputBody;
    using (var reader = new System.IO.StreamReader(
        httpContext.Request.Body, System.Text.Encoding.UTF8))
    {
        inputBody = reader.ReadToEnd();
    }
    

    경고: 파이프라인 끝의 처리기 미들웨어에서만 사용합니다. 본문은 요청당 한 번만 읽을 수 있습니다.

HttpResponse 속성

  • HttpResponse.Status / HttpResponse.StatusDescriptionHttpResponse.StatusCode

    // using Microsoft.AspNetCore.Http;
    httpContext.Response.StatusCode = StatusCodes.Status200OK;
    
  • HttpResponse.ContentEncoding / HttpResponse.ContentTypeHttpResponse.ContentType

    // using Microsoft.Net.Http.Headers;
    var mediaType = new MediaTypeHeaderValue("application/json");
    mediaType.Encoding = System.Text.Encoding.UTF8;
    httpContext.Response.ContentType = mediaType.ToString();
    
  • HttpResponse.ContentTypeHttpResponse.ContentType

    httpContext.Response.ContentType = "text/html";
    
  • HttpResponse.OutputHttpResponseWritingExtensions.WriteAsync

    string responseContent = GetResponseContent();
    await httpContext.Response.WriteAsync(responseContent);
    
  • HttpResponse.TransmitFile요청 기능 참조

    서비스 파일은 ASP.NET Core의 요청 기능에서 설명합니다.

  • HttpResponse.HeadersHttpResponse.OnStarting

    // using Microsoft.AspNet.Http.Headers;
    // using Microsoft.Net.Http.Headers;
    
    private Task SetHeaders(object context)
    {
        var httpContext = (HttpContext)context;
    
        // Set header with single value
        httpContext.Response.Headers["ResponseHeaderName"] = "headerValue";
    
        // Set header with multiple values
        string[] responseHeaderValues = new string[] { "headerValue1", "headerValue1" };
        httpContext.Response.Headers["ResponseHeaderName"] = responseHeaderValues;
    
        // Translating ASP.NET 4's HttpContext.Response.RedirectLocation  
        httpContext.Response.Headers[HeaderNames.Location] = "http://www.example.com";
        // Or
        httpContext.Response.Redirect("http://www.example.com");
    
        // GetTypedHeaders extension method provides strongly typed access to many headers
        var responseHeaders = httpContext.Response.GetTypedHeaders();
    
        // Translating ASP.NET 4's HttpContext.Response.CacheControl 
        responseHeaders.CacheControl = new CacheControlHeaderValue
        {
            MaxAge = new System.TimeSpan(365, 0, 0, 0)
            // Many more properties available 
        };
    
        // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
        return Task.FromResult(0);
    }
    

    응답이 시작되기 전에 콜백 패턴을 사용하여 헤더를 설정해야 합니다.

  • HttpResponse.CookiesHttpResponse.OnStarting

    private Task SetCookies(object context)
    {
        var httpContext = (HttpContext)context;
    
        IResponseCookies responseCookies = httpContext.Response.Cookies;
    
        responseCookies.Append("cookie1name", "cookie1value");
        responseCookies.Append("cookie2name", "cookie2value",
            new CookieOptions { Expires = System.DateTime.Now.AddDays(5), HttpOnly = true });
    
        // If you use .NET Framework 4.6+, Task.CompletedTask will be a bit faster
        return Task.FromResult(0); 
    }
    

    응답이 시작되기 전에 콜백 패턴을 사용하여 쿠키를 설정해야 합니다.

  • 응답 헤더 설정:

    public async Task Invoke(HttpContext httpContext)
    {
        // Set callback to execute before response starts
        httpContext.Response.OnStarting(SetHeaders, state: httpContext);
        // ... rest of middleware logic
    }
    
  • 응답 쿠키 설정:

public async Task Invoke(HttpContext httpContext)
{
    // Set callbacks to execute before response starts
    httpContext.Response.OnStarting(SetCookies, state: httpContext);
    httpContext.Response.OnStarting(SetHeaders, state: httpContext);
    // ... rest of middleware logic
}

System.Web 어댑터

비고

이렇게 하면 System.Web 어댑터를 사용하여 마이그레이션을 간소화할 수 있습니다.

공유 라이브러리에서 광범위한 HttpContext 사용량이 있거나 코드 변경을 최소화하려는 증분 마이그레이션을 수행할 때 이 방법을 선택합니다.

System.Web 어댑터는 ASP.NET Core 애플리케이션에서 친숙한 HttpContext API를 사용할 수 있는 호환성 계층을 제공합니다. 이 방법은 다음과 같은 경우에 특히 유용합니다.

  • 사용하는 공유 라이브러리가 있습니다. HttpContext
  • 증분 마이그레이션을 수행하고 있습니다.
  • 마이그레이션 프로세스 중에 코드 변경을 최소화하려고 합니다.

System.Web 어댑터 사용의 이점

  • 최소 코드 변경: 기존 System.Web.HttpContext 사용 패턴 유지
  • 공유 라이브러리: 라이브러리는 ASP.NET Framework와 ASP.NET Core 모두에서 작동할 수 있습니다.
  • 증분 마이그레이션: 공유 종속성을 중단하지 않고 애플리케이션을 하나씩 마이그레이션
  • 빠른 마이그레이션: 복잡한 애플리케이션을 마이그레이션하는 데 필요한 시간 단축

고려 사항

  • 성능: 좋은 반면 어댑터는 네이티브 ASP.NET Core API에 비해 약간의 오버헤드를 발생합니다.
  • 기능 패리티: 어댑터를 통해 모든 HttpContext 기능을 사용할 수 있는 것은 아닙니다.
  • 장기 전략: 최상의 성능을 위해 최종적으로 네이티브 ASP.NET Core API로 마이그레이션하는 것이 좋습니다.

System.Web 어댑터에 대한 자세한 내용은 System.Web 어댑터 설명서를 참조하세요.

추가 리소스