ASP.NET Core에서 세션 및 상태 관리
작성자: Rick Anderson, Kirk Larkin, Diana LaRose
HTTP는 상태 비저장 프로토콜입니다. 기본적으로 HTTP 요청은 사용자 값을 유지하지 않는 독립적인 메시지입니다. 이 문서에서는 요청 간에 사용자 데이터를 유지하는 여러 가지 방법을 설명합니다.
Blazor 이 문서의 지침을 추가하거나 대체하는 상태 관리 지침은 ASP.NET Core Blazor 상태 관리를 참조하세요.
상태 관리
상태는 여러 방법을 사용하여 저장할 수 있습니다. 각 방법은 이 문서의 뒷부분에 설명되어 있습니다.
스토리지 접근 방식 | 스토리지 메커니즘 |
---|---|
쿠키 | HTTP 쿠키. 서버 쪽 앱 코드를 사용하여 저장된 데이터를 포함할 수 있습니다. |
세션 상태 | HTTP 쿠키 및 서버 쪽 앱 코드 |
TempData | HTTP 쿠키 또는 세션 상태 |
쿼리 문자열 | HTTP 쿼리 문자열 |
숨겨진 필드 | HTTP 양식 필드 |
HttpContext.Items | 서버 쪽 앱 코드 |
캐시 | 서버 쪽 앱 코드 |
SignalR/Blazor Server 및 HTTP 컨텍스트 기반 상태 관리
SignalR 앱은 안정적인 HTTP 컨텍스트를 사용하여 정보를 저장하는 세션 상태 및 기타 상태 관리 접근 방식을 사용하면 안 됩니다. SignalR 앱은 허브에서 Context.Items
의 연결 상태별로 저장할 수 있습니다. Blazor Server 앱에 대한 자세한 내용 및 대체 상태 관리 방법은 ASP.NET Core Blazor 상태 관리를 참조하세요.
쿠키
쿠키는 요청 간에 데이터를 저장합니다. 쿠키는 모든 요청과 함께 전송되므로 해당 크기는 최소로 유지되어야 합니다. 이상적으로 식별자만 앱에 저장된 데이터와 함께 cookie에 저장되어야 합니다. 대부분의 브라우저는 cookie 크기를 4,096바이트로 제한합니다. 제한된 수의 쿠키만 각 도메인에 사용할 수 있습니다.
쿠키는 변조될 수 있기 때문에 앱에서 유효성을 검사해야 합니다. 쿠키는 사용자가 삭제할 수 있으며 클라이언트에서 만료됩니다. 그러나 쿠키는 일반적으로 클라이언트에서 데이터 지속성의 가장 안정적인 형태입니다.
쿠키는 종종 개인 설정에 사용됩니다. 여기서 콘텐츠는 알려진 사용자에 대해 사용자 지정됩니다. 사용자만 식별되고 대부분의 경우 인증되지 않습니다. cookie는 사용자 이름, 계정 이름 또는 고유한 사용자 ID(예: GUID)를 저장할 수 있습니다. cookie를 사용하여 선호하는 웹 사이트 배경색과 같은 사용자의 맞춤형 설정에 액세스할 수 있습니다.
쿠키를 발급하고 문제를 처리 privacy 할 때 유럽 연합 GDPR(일반 데이터 보호 규정)을 참조하세요. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.
세션 상태
세션 상태는 사용자가 웹앱을 탐색하는 동안 사용자 데이터를 스토리지하기 위한 ASP.NET Core 시나리오입니다. 세션 상태는 앱에서 유지 관리하는 저장소를 사용하여 클라이언트의 요청 간에 데이터를 유지합니다. 세션 데이터는 캐시에 백업되며 임시 데이터로 간주됩니다. 세션 데이터 없이도 사이트가 계속 작동해야 합니다. 중요한 애플리케이션 데이터는 사용자 데이터베이스에 저장되고 성능 최적화로 세션에 캐시되어야 합니다.
SignalR 허브가 HTTP 컨텍스트와 독립적으로 실행될 수 있으므로, 세션은 SignalR 앱에서 지원되지 않습니다. 예를 들어, 허브에서 긴 폴링 요청이 HTTP 컨텍스트 수명을 초과하여 계속 열려 있을 경우 이 문제가 발생할 수 있습니다.
ASP.NET Core는 세션 ID를 포함하는 cookie를 클라이언트에 제공하여 세션 상태를 유지 관리합니다. cookie 세션 ID:
- 각 요청과 함께 앱에 전송됩니다.
- 앱에서 세션 데이터를 가져오는 데 사용됩니다.
세션 상태는 다음과 같은 동작을 보여 줍니다.
- 세션 cookie는 브라우저와 관련이 있습니다. 세션은 브라우저 간에 공유되지 않습니다.
- 세션 쿠키는 브라우저 세션이 끝나면 삭제됩니다.
- cookie가 만료된 세션에 대해 수신되면 동일한 세션 cookie를 사용하는 새 세션이 생성됩니다.
- 빈 세션은 유지되지 않습니다. 요청 간에 세션을 유지하려면 세션에 하나 이상의 값이 설정되어 있어야 합니다. 세션이 유지되지 않으면 새 요청마다 새 세션 ID가 생성됩니다.
- 앱은 마지막 요청 이후 제한된 시간 동안 세션을 유지합니다. 앱은 세션 시간 제한을 설정하거나 20분의 기본값을 사용합니다. 세션 상태는 사용자 데이터를 저장하는 데 적합합니다.
- 특정 세션과 관련이 있습니다.
- 세션 간에 데이터의 영구 스토리지가 필요하지 않습니다.
- 세션 데이터는 ISession.Clear 구현이 호출되거나 세션이 만료될 때 삭제됩니다.
- 클라이언트 브라우저가 닫혔거나 세션 cookie가 삭제 또는 클라이언트에서 만료되었을 때 앱 코드에 이를 알려주는 기본 메커니즘은 없습니다.
- 기본적으로 세션 상태 쿠키는 필수로 표시되어 있지 않습니다. 사이트 방문자가 추적을 허용하지 않는 한, 세션 상태는 작동하지 않습니다. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.
- 참고: ASP.NET Framework의 쿠키 없는 세션 기능은 안전하지 않은 것으로 간주되어 세션 고정 공격으로 이어질 수 있으므로 대체되지 않습니다.
Warning
중요한 데이터를 세션 상태에 저장하지 마세요. 사용자는 브라우저를 닫지 않고 세션 cookie를 지울 수 있습니다. 일부 브라우저는 브라우저 창이 닫혀도 유효한 세션 쿠키를 유지 관리합니다. 세션을 단일 사용자로 제한하지 못할 수도 있습니다. 다음 사용자가 동일한 세션 cookie로 앱을 계속 검색할 수 있습니다.
메모리 내 캐시 공급자는 앱이 있는 서버의 메모리에 세션 데이터를 저장합니다. 서버 팜 시나리오:
- 고정 세션을 사용하여 각 세션을 개별 서버의 특정 앱 인스턴스에 연결합니다. Azure App Service는 ARR(애플리케이션 요청 라우팅)을 사용하여 기본적으로 고정 세션을 적용합니다. 그러나 고정 세션은 확장성에 영향을 주고 웹앱 업데이트를 복잡하게 만들 수 있습니다. 더 나은 방법은 고정 세션이 필요 없는 Redis 또는 SQL Server 분산 캐시를 사용하는 것입니다. 자세한 내용은 ASP.NET Core의 분산 캐싱을 참조하세요.
- 세션 cookie는 IDataProtector를 통해 암호화됩니다. 데이터 보호는 각 컴퓨터에서 세션 쿠키를 읽을 수 있도록 올바르게 구성되어야 합니다. 자세한 내용은 ASP.NET Core 데이터 보호 개요 및 키 스토리지 공급자를 참조하세요.
세션 상태 구성
세션 상태를 관리하기 위한 미들웨어가 프레임워크에 포함됩니다. 세션 미들웨어를 활성화하려면 Program.cs
은 다음을 포함해야 합니다.
- IDistributedCache 메모리 캐시 중 하나.
IDistributedCache
구현은 세션에 대한 백업 저장소로 사용됩니다. 자세한 내용은 ASP.NET Core의 분산 캐싱을 참조하세요. - AddSession 호출
- UseSession 호출
다음 코드에서는 IDistributedCache
의 기본 메모리 내 구현을 사용하여 메모리 내 세션 공급자를 설정하는 방법을 보여 줍니다.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
위의 코드는 테스트를 간소화하기 위해 짧은 시간 제한을 설정합니다.
미들웨어의 순서가 중요합니다. UseRouting
이후, MapRazorPages
및 MapDefaultControllerRoute
이전에 UseSession
을 호출합니다. 미들웨어 순서를 참조하세요.
HttpContext.Session은 세션 상태가 구성된 후에 사용할 수 있습니다.
UseSession
이 호출되기 전에 HttpContext.Session
에 액세스할 수 없습니다.
앱이 응답 스트림에 쓰기 시작한 후에는 새 세션 cookie가 있는 새 세션을 만들 수 없습니다. 예외는 웹 서버 로그에 기록되며 브라우저에는 표시되지 않습니다.
세션 상태를 비동기적으로 로드
ASP.NET Core에서 기본 세션 공급자는 ISession.LoadAsync 메서드가 TryGetValue, Set 또는 Remove 메서드 전에 명시적으로 호출된 경우에만 기본 IDistributedCache 백업 저장소에서 비동기적으로 세션 레코드를 로드합니다. LoadAsync
가 먼저 호출되지 않은 경우 기본 세션 레코드가 동기적으로 로드되며, 이는 크기에 따라 성능 저하를 초래할 수 있습니다.
이 패턴을 앱에 적용하려면 LoadAsync
메서드가 TryGetValue
, Set
또는 Remove
이전에 호출되지 않은 경우 예외를 throw하는 버전으로 DistributedSessionStore 및 DistributedSession 구현을 래핑합니다. 서비스 컨테이너에 래핑된 버전을 등록합니다.
세션 옵션
세션 기본값을 재정의하려면 SessionOptions를 사용합니다.
옵션 | 설명 |
---|---|
Cookie | cookie를 만드는 데 사용되는 설정을 결정합니다. Name 기본값은 SessionDefaults.CookieName(.AspNetCore.Session )입니다. Path 기본값은 SessionDefaults.CookiePath(/ )입니다. SameSite 기본값은 SameSiteMode.Lax(1 )입니다. HttpOnly 기본값은 true 입니다. IsEssential 기본값은 false 입니다. |
IdleTimeout | IdleTimeout 은 콘텐츠가 삭제되기 전까지 세션이 유휴 상태일 수 있는 시간을 나타냅니다. 각 세션 액세스는 시간 제한을 다시 설정합니다. 이 설정은 cookie가 아닌 세션의 콘텐츠에만 적용됩니다. 기본값은 20분입니다. |
IOTimeout | 저장소에서 세션을 로드하거나 저장소로 다시 커밋할 수 있는 최대 시간입니다. 이 설정은 비동기 작업에만 적용될 수 있습니다. 이 시간 제한은 InfiniteTimeSpan을 사용하여 사용하지 않도록 설정할 수 있습니다. 기본값은 1분입니다. |
세션은 cookie를 사용하여 단일 브라우저에서 요청을 추적하고 식별합니다. 기본적으로 이 cookie는 .AspNetCore.Session
이라고 하며 /
의 경로를 사용합니다. cookie 기본값은 도메인을 지정하지 않으므로 페이지에서 클라이언트 쪽 스크립트에 사용할 수 없습니다(HttpOnly 기본값이 true
이므로).
cookie 세션 기본값을 재정의하려면 SessionOptions를 사용합니다.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
builder.Services.AddControllersWithViews();
builder.Services.AddDistributedMemoryCache();
builder.Services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.IsEssential = true;
});
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
앱은 IdleTimeout 속성을 사용하여 서버 캐시의 콘텐츠가 중단되기 전에 유휴 상태일 수 있는 세션의 기간을 결정합니다. 이 속성은 cookie 만료와 무관합니다. 세션 미들웨어를 통해 전달되는 각 요청은 시간 제한을 다시 설정합니다.
세션 상태는 잠그지 않음입니다. 두 요청이 동시에 세션의 콘텐츠를 수정하려고 하는 경우 마지막 요청이 첫 번째 요청을 재정의합니다. Session
은 일관된 세션으로 구현됩니다. 즉, 모든 콘텐츠는 함께 저장됩니다. 두 요청이 서로 다른 세션 값을 수정하려고 할 때 마지막 요청이 첫 번째 요청에 의해 수행된 세션 변경 내용을 재정의할 수 있습니다.
세션 값 설정 및 가져오기
세션 상태는 HttpContext.Session이 포함된 Razor Pages PageModel 클래스 또는 MVC Controller 클래스에서 액세스됩니다. 이 속성은 ISession 구현입니다.
ISession
구현은 정수 및 문자열 값을 설정 및 검색하는 몇 가지 확장 메서드를 제공합니다. 확장 메서드는 Microsoft.AspNetCore.Http 네임스페이스에 있습니다.
ISession
확장명 메서드:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
다음 예제에서는 Razor Pages 페이지에서 IndexModel.SessionKeyName
키(샘플 앱의 _Name
)의 세션 값을 검색합니다.
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
다음 예제에서는 정수와 문자열을 설정하고 가져오는 방법을 보여 줍니다.
public class IndexModel : PageModel
{
public const string SessionKeyName = "_Name";
public const string SessionKeyAge = "_Age";
private readonly ILogger<IndexModel> _logger;
public IndexModel(ILogger<IndexModel> logger)
{
_logger = logger;
}
public void OnGet()
{
if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
{
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 73);
}
var name = HttpContext.Session.GetString(SessionKeyName);
var age = HttpContext.Session.GetInt32(SessionKeyAge).ToString();
_logger.LogInformation("Session Name: {Name}", name);
_logger.LogInformation("Session Age: {Age}", age);
}
}
다음 태그는 Razor 페이지에 세션 값을 표시합니다.
@page
@model PrivacyModel
@{
ViewData["Title"] = "Privacy Policy";
}
<h1>@ViewData["Title"]</h1>
<div class="text-center">
<p><b>Name:</b> @HttpContext.Session.GetString("_Name");<b>Age:
</b> @HttpContext.Session.GetInt32("_Age").ToString()</p>
</div>
메모리 내 캐시를 사용하는 경우에도, 분산된 캐시 시나리오를 사용하려면 모든 세션 데이터를 직렬화해야 합니다. 문자열 및 정수 직렬 변환기는 ISession의 확장 메서드가 제공합니다. 복합 형식은 JSON과 같은 다른 메커니즘을 사용하여 사용자가 직렬화해야 합니다.
다음 샘플 코드를 사용하여 개체를 직렬화할 수 있습니다.
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T? Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
다음 예제는 SessionExtensions
클래스를 사용하여 직렬화 가능 개체를 설정하고 가져오는 방법을 보여 줍니다.
using Microsoft.AspNetCore.Mvc.RazorPages;
using Web.Extensions; // SessionExtensions
namespace SessionSample.Pages
{
public class Index6Model : PageModel
{
const string SessionKeyTime = "_Time";
public string? SessionInfo_SessionTime { get; private set; }
private readonly ILogger<Index6Model> _logger;
public Index6Model(ILogger<Index6Model> logger)
{
_logger = logger;
}
public void OnGet()
{
var currentTime = DateTime.Now;
// Requires SessionExtensions from sample.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}
_logger.LogInformation("Current Time: {Time}", currentTime);
_logger.LogInformation("Session Time: {Time}",
HttpContext.Session.Get<DateTime>(SessionKeyTime));
}
}
}
Warning
직렬화된 개체에서 발생할 수 있는 많은 문제가 있으므로 세션에 라이브 개체를 저장하는 것은 주의해서 사용해야 합니다. 자세한 내용은 개체(dotnet/aspnetcore #18159)를 저장할 수 있도록 세션이 허용되어야 하므로 참조하세요.
TempData
ASP.NET Core는 Razor Pages TempData 또는 컨트롤러 TempData를 공개합니다. 이 속성은 다른 요청에서 읽혀질 때까지만 데이터를 저장합니다. Keep(문자열) 및 Peek(문자열) 메서드를 사용하면 요청이 끝날 때 삭제하지 않고 데이터를 검사할 수 있습니다. Keep은 사전의 모든 항목을 보존하도록 표시합니다. TempData
인 경우 다음과 같습니다.
- 데이터가 둘 이상의 요청에 필요한 경우 리디렉션에 유용합니다.
TempData
공급자가 쿠키 또는 세션 상태를 사용하여 구현합니다.
TempData 샘플
고객을 만드는 다음 페이지를 살펴보겠습니다.
public class CreateModel : PageModel
{
private readonly RazorPagesContactsContext _context;
public CreateModel(RazorPagesContactsContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./IndexPeek");
}
}
다음 페이지는 TempData["Message"]
를 표시합니다.
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
위 태그에서 Peek
가 사용되기 때문에 요청 끝에서 TempData["Message"]
가 삭제되지 않습니다. 페이지를 새로 고치면 TempData["Message"]
의 내용이 표시됩니다.
다음 태그는 위 코드와 비슷하지만 Keep
을 사용하여 요청 끝에서 데이터를 유지합니다.
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
IndexPeek 페이지와 IndexKeep 페이지 사이를 이동해도 TempData["Message"]
가 삭제되지 않습니다.
다음 코드는 TempData["Message"]
를 표시하지만 요청 끝에서 TempData["Message"]
가 삭제됩니다.
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
TempData 공급자
cookie-based TempData 공급자는 기본적으로 TempData를 쿠키에 저장하는 데 사용됩니다.
cookie 데이터는 IDataProtector를 사용하여 암호화되고, Base64UrlTextEncoder로 인코딩된 후 청크 분할됩니다. 암호화 및 청크로 인해 최대 cookie 크기는 4,096바이트 미만입니다. cookie 암호화된 데이터를 압축하면 보안 문제(예: CRIME BREACH 공격)가 발생할 수 있으므로 데이터가 압축되지 않습니다. cookie 기반 TempData 공급자에 대한 자세한 내용은 CookieTempDataProvider를 참조하세요.
TempData 공급자 선택
TempData 공급자를 선택하는 데는 다음과 같은 몇 가지 고려 사항이 수반됩니다.
- 앱이 이미 세션 상태를 사용합니까? 그런 경우, 데이터 크기를 제외하고 세션 상태 TempData 공급자 사용과 관련해서 앱에 부과되는 추가 비용은 없습니다.
- 앱이 비교적 적은 양의 데이터(최대 500바이트)에만 TempData를 제한적으로 사용합니까? 그런 경우 cookie TempData 공급자는 TempData를 전달하는 각 요청에 적은 비용을 추가합니다. 그렇지 않은 경우 세션 상태 TempData 공급자는 TempData가 사용될 때까지 각 요청에서 많은 양의 데이터를 왕복 작업하지 않도록 하는 데 도움이 될 수 있습니다.
- 앱이 여러 서버의 서버 팜에서 실행됩니까? 그런 경우 데이터 보호 외부에서 cookie TempData 공급자를 사용하는 데 필요한 추가 구성은 없습니다. 자세한 내용은 ASP.NET Core 데이터 보호 개요 및 키 스토리지 공급자를 참조하세요.
웹 브라우저와 같은 대부분의 웹 클라이언트는 각 cookie 쿠키의 최대 크기와 총 쿠키 수에 제한을 적용합니다. cookie TempData 공급자를 사용하는 경우, 앱이 해당 제한을 넘지 않도록 확인합니다. 데이터의 총 크기를 고려합니다. 암호화 및 청크 분할로 인한 cookie 크기 증가를 고려합니다.
TempData 공급자 구성
cookie 기반 TempData 공급자는 기본적으로 활성화됩니다.
세션 기반 TempData 공급자를 사용하도록 설정하려면 AddSessionStateTempDataProvider 확장 메서드를 사용합니다. AddSessionStateTempDataProvider
를 한 번만 호출하면 됩니다.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages()
.AddSessionStateTempDataProvider();
builder.Services.AddControllersWithViews()
.AddSessionStateTempDataProvider();
builder.Services.AddSession();
var app = builder.Build();
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseSession();
app.MapRazorPages();
app.MapDefaultControllerRoute();
app.Run();
쿼리 문자열
제한된 양의 데이터는 새 요청의 쿼리 문자열에 추가하여 한 요청에서 다른 요청으로 전달할 수 있습니다. 이는 이메일 또는 소셜 네트워크를 통해 공유되도록 포함된 상태가 있는 링크를 허용하는 영구적인 방식으로 상태를 캡처하는 데 유용합니다. URL 쿼리 문자열은 공용이므로 중요한 데이터에 쿼리 문자열을 사용하지 마세요.
의도하지 않은 공유 외에도, 쿼리 문자열에 데이터를 포함하면 앱이 CSRF(교차 사이트 요청 위조) 공격에 노출될 수 있습니다. 유지된 모든 세션 상태를 CSRF 공격으로부터 보호해야 합니다. 자세한 내용은 ASP.NET Core에서 교차 사이트 요청 위조(XSRF/CSRF) 공격 방지를 참조하세요.
숨겨진 필드
데이터는 숨겨진 양식 필드에 저장되고 다음 요청에서 다시 게시될 수 있습니다. 이는 다중 페이지 폼에서 일반적입니다. 클라이언트는 잠재적으로 데이터를 변조할 수 있으므로 앱은 항상 숨겨진 필드에 저장된 데이터의 유효성을 다시 검사해야 합니다.
HttpContext.Items
HttpContext.Items 컬렉션은 단일 요청을 처리하는 동안 데이터를 저장하는 데 사용됩니다. 컬렉션의 콘텐츠는 요청이 처리된 후 삭제됩니다. Items
컬렉션은 구성 요소 또는 미들웨어가 요청 중에 다른 시점에서 작동하고 매개 변수를 전달할 직접적인 방법이 없는 경우에 통신을 지원하기 위해 자주 사용됩니다.
다음 예제에서 미들웨어는 isVerified
를 Items
컬렉션에 추가합니다.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
ILogger logger = app.Logger;
app.Use(async (context, next) =>
{
// context.Items["isVerified"] is null
logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
context.Items["isVerified"] = true;
await next.Invoke();
});
app.Use(async (context, next) =>
{
// context.Items["isVerified"] is true
logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
await next.Invoke();
});
app.MapGet("/", async context =>
{
await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});
app.Run();
단일 앱에서만 사용되는 미들웨어의 경우 고정된 string
키를 사용하면 키 충돌이 발생할 가능성이 낮습니다. 그러나 키 충돌이 발생하지 않도록 object
를 항목 키로 사용할 수 있습니다. 이 방법은 앱 간에 공유되는 미들웨어에 특히 유용하며 또한 코드에서 키 문자열을 사용하지 않아도 된다는 이점이 있습니다. 다음 예제에서는 미들웨어 클래스에 정의된 고유한 object
키를 사용하는 방법을 보여 줍니다.
public class HttpContextItemsMiddleware
{
private readonly RequestDelegate _next;
public static readonly object HttpContextItemsMiddlewareKey = new();
public HttpContextItemsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";
await _next(httpContext);
}
}
public static class HttpContextItemsMiddlewareExtensions
{
public static IApplicationBuilder
UseHttpContextItemsMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HttpContextItemsMiddleware>();
}
}
다른 코드는 미들웨어 클래스에 의해 노출된 키를 사용하여 HttpContext.Items
에 저장된 값에 액세스할 수 있습니다.
public class Index2Model : PageModel
{
private readonly ILogger<Index2Model> _logger;
public Index2Model(ILogger<Index2Model> logger)
{
_logger = logger;
}
public void OnGet()
{
HttpContext.Items
.TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
out var middlewareSetValue);
_logger.LogInformation("Middleware value {MV}",
middlewareSetValue?.ToString() ?? "Middleware value not set!");
}
}
캐시
캐싱은 데이터 저장 및 검색하는 효율적인 방법입니다. 앱은 캐시된 항목의 수명을 제어할 수 있습니다. 자세한 내용은 ASP.NET Core의 응답 캐싱을 참조하세요.
캐시된 데이터는 특정 요청, 사용자 또는 세션과 연관되지 않습니다. 다른 사용자 요청을 통해 검색될 수 있는 사용자별 데이터를 캐시하면 안 됩니다.
애플리케이션 전반의 데이터를 캐시하려면 ASP.NET Core 메모리 내 캐시를 참조하세요.
세션 상태 확인
ISession.IsAvailable 은 일시적인 오류를 확인하기 위한 것입니다. 세션 미들웨어가 실행되기 전에 호출 IsAvailable
하면 .InvalidOperationException
세션 가용성을 테스트해야 하는 라이브러리는 다음을 사용할 HttpContext.Features.Get<ISessionFeature>()?.Session != null
수 있습니다.
일반 오류
"'Microsoft.AspNetCore.Session.DistributedSessionStore'를 활성화하려고 시도하는 동안 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' 형식에 대한 서비스를 확인할 수 없습니다."
이 오류는 일반적으로 하나 이상의
IDistributedCache
구현을 구성하지 못한 경우에 발생합니다. 자세한 내용은 ASP.NET Core의 분산 캐싱 및 ASP.NET Core 메모리 내 캐시를 참조하세요.
세션 미들웨어가 세션을 유지하지 못한 경우
- 미들웨어가 예외를 기록하고 요청은 정상적으로 계속 진행됩니다.
- 이로 인해 예기치 않은 동작이 발생합니다.
백업 저장소를 사용할 수 없는 경우 세션 미들웨어가 세션을 유지하지 못할 수 있습니다. 예를 들어 사용자는 세션에 쇼핑 카트를 저장합니다. 사용자가 카트에 항목을 추가하지만 커밋이 실패합니다. 앱은 실패에 대해 알지 못하므로 항목이 카트에 추가되었다고 보고하지만, 이는 사실이 아닙니다.
오류를 확인하는 권장 방법은 앱이 세션에 기록을 마쳤을 때 await feature.Session.CommitAsync
를 호출하는 것입니다. 백업 저장소를 사용할 수 없는 경우 CommitAsync에서 예외를 throw합니다. CommitAsync
가 실패하면 앱에서 예외를 처리할 수 있습니다. 데이터 저장소를 사용할 수 없는 경우에는 동일한 조건에서 LoadAsync가 throw합니다.
추가 리소스
작성자: Rick Anderson, Kirk Larkin, Diana LaRose
HTTP는 상태 비저장 프로토콜입니다. 기본적으로 HTTP 요청은 사용자 값을 유지하지 않는 독립적인 메시지입니다. 이 문서에서는 요청 간에 사용자 데이터를 유지하는 여러 가지 방법을 설명합니다.
상태 관리
상태는 여러 방법을 사용하여 저장할 수 있습니다. 각 방법은 이 문서의 뒷부분에 설명되어 있습니다.
스토리지 접근 방식 | 스토리지 메커니즘 |
---|---|
쿠키 | HTTP 쿠키. 서버 쪽 앱 코드를 사용하여 저장된 데이터를 포함할 수 있습니다. |
세션 상태 | HTTP 쿠키 및 서버 쪽 앱 코드 |
TempData | HTTP 쿠키 또는 세션 상태 |
쿼리 문자열 | HTTP 쿼리 문자열 |
숨겨진 필드 | HTTP 양식 필드 |
HttpContext.Items | 서버 쪽 앱 코드 |
캐시 | 서버 쪽 앱 코드 |
SignalR/Blazor Server 및 HTTP 컨텍스트 기반 상태 관리
SignalR 앱은 안정적인 HTTP 컨텍스트를 사용하여 정보를 저장하는 세션 상태 및 기타 상태 관리 접근 방식을 사용하면 안 됩니다. SignalR 앱은 허브에서 Context.Items
의 연결 상태별로 저장할 수 있습니다. Blazor Server 앱에 대한 자세한 내용 및 대체 상태 관리 방법은 ASP.NET Core Blazor 상태 관리를 참조하세요.
쿠키
쿠키는 요청 간에 데이터를 저장합니다. 쿠키는 모든 요청과 함께 전송되므로 해당 크기는 최소로 유지되어야 합니다. 이상적으로 식별자만 앱에 저장된 데이터와 함께 cookie에 저장되어야 합니다. 대부분의 브라우저는 cookie 크기를 4,096바이트로 제한합니다. 제한된 수의 쿠키만 각 도메인에 사용할 수 있습니다.
쿠키는 변조될 수 있기 때문에 앱에서 유효성을 검사해야 합니다. 쿠키는 사용자가 삭제할 수 있으며 클라이언트에서 만료됩니다. 그러나 쿠키는 일반적으로 클라이언트에서 데이터 지속성의 가장 안정적인 형태입니다.
쿠키는 종종 개인 설정에 사용됩니다. 여기서 콘텐츠는 알려진 사용자에 대해 사용자 지정됩니다. 사용자만 식별되고 대부분의 경우 인증되지 않습니다. cookie는 사용자 이름, 계정 이름 또는 고유한 사용자 ID(예: GUID)를 저장할 수 있습니다. cookie를 사용하여 선호하는 웹 사이트 배경색과 같은 사용자의 맞춤형 설정에 액세스할 수 있습니다.
쿠키를 발급하고 문제를 처리 privacy 할 때 유럽 연합 GDPR(일반 데이터 보호 규정)을 참조하세요. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.
세션 상태
세션 상태는 사용자가 웹앱을 탐색하는 동안 사용자 데이터를 스토리지하기 위한 ASP.NET Core 시나리오입니다. 세션 상태는 앱에서 유지 관리하는 저장소를 사용하여 클라이언트의 요청 간에 데이터를 유지합니다. 세션 데이터는 캐시에 백업되며 임시 데이터로 간주됩니다. 세션 데이터 없이도 사이트가 계속 작동해야 합니다. 중요한 애플리케이션 데이터는 사용자 데이터베이스에 저장되고 성능 최적화로 세션에 캐시되어야 합니다.
SignalR 허브가 HTTP 컨텍스트와 독립적으로 실행될 수 있으므로, 세션은 SignalR 앱에서 지원되지 않습니다. 예를 들어, 허브에서 긴 폴링 요청이 HTTP 컨텍스트 수명을 초과하여 계속 열려 있을 경우 이 문제가 발생할 수 있습니다.
ASP.NET Core는 세션 ID를 포함하는 cookie를 클라이언트에 제공하여 세션 상태를 유지 관리합니다. cookie 세션 ID:
- 각 요청과 함께 앱에 전송됩니다.
- 앱에서 세션 데이터를 가져오는 데 사용됩니다.
세션 상태는 다음과 같은 동작을 보여 줍니다.
- 세션 cookie는 브라우저와 관련이 있습니다. 세션은 브라우저 간에 공유되지 않습니다.
- 세션 쿠키는 브라우저 세션이 끝나면 삭제됩니다.
- cookie가 만료된 세션에 대해 수신되면 동일한 세션 cookie를 사용하는 새 세션이 생성됩니다.
- 빈 세션은 유지되지 않습니다. 요청 간에 세션을 유지하려면 세션에 하나 이상의 값이 설정되어 있어야 합니다. 세션이 유지되지 않으면 새 요청마다 새 세션 ID가 생성됩니다.
- 앱은 마지막 요청 이후 제한된 시간 동안 세션을 유지합니다. 앱은 세션 시간 제한을 설정하거나 20분의 기본값을 사용합니다. 세션 상태는 사용자 데이터를 저장하는 데 적합합니다.
- 특정 세션과 관련이 있습니다.
- 세션 간에 데이터의 영구 스토리지가 필요하지 않습니다.
- 세션 데이터는 ISession.Clear 구현이 호출되거나 세션이 만료될 때 삭제됩니다.
- 클라이언트 브라우저가 닫혔거나 세션 cookie가 삭제 또는 클라이언트에서 만료되었을 때 앱 코드에 이를 알려주는 기본 메커니즘은 없습니다.
- 기본적으로 세션 상태 쿠키는 필수로 표시되어 있지 않습니다. 사이트 방문자가 추적을 허용하지 않는 한, 세션 상태는 작동하지 않습니다. 자세한 내용은 ASP.NET Core의 GDPR(일반 데이터 보호 규정) 지원을 참조하세요.
Warning
중요한 데이터를 세션 상태에 저장하지 마세요. 사용자는 브라우저를 닫지 않고 세션 cookie를 지울 수 있습니다. 일부 브라우저는 브라우저 창이 닫혀도 유효한 세션 쿠키를 유지 관리합니다. 세션을 단일 사용자로 제한하지 못할 수도 있습니다. 다음 사용자가 동일한 세션 cookie로 앱을 계속 검색할 수 있습니다.
메모리 내 캐시 공급자는 앱이 있는 서버의 메모리에 세션 데이터를 저장합니다. 서버 팜 시나리오:
- 고정 세션을 사용하여 각 세션을 개별 서버의 특정 앱 인스턴스에 연결합니다. Azure App Service는 ARR(애플리케이션 요청 라우팅)을 사용하여 기본적으로 고정 세션을 적용합니다. 그러나 고정 세션은 확장성에 영향을 주고 웹앱 업데이트를 복잡하게 만들 수 있습니다. 더 나은 방법은 고정 세션이 필요 없는 Redis 또는 SQL Server 분산 캐시를 사용하는 것입니다. 자세한 내용은 ASP.NET Core의 분산 캐싱을 참조하세요.
- 세션 cookie는 IDataProtector를 통해 암호화됩니다. 데이터 보호는 각 컴퓨터에서 세션 쿠키를 읽을 수 있도록 올바르게 구성되어야 합니다. 자세한 내용은 ASP.NET Core 데이터 보호 개요 및 키 스토리지 공급자를 참조하세요.
세션 상태 구성
Microsoft.AspNetCore.Session 패키지와 관련해서 다음 사항을 확인합니다.
- 프레임워크를 통해 암시적으로 포함됩니다.
- 세션 상태를 관리하기 위한 미들웨어를 제공합니다.
세션 미들웨어를 활성화하려면 Startup
은 다음을 포함해야 합니다.
- IDistributedCache 메모리 캐시 중 하나.
IDistributedCache
구현은 세션에 대한 백업 저장소로 사용됩니다. 자세한 내용은 ASP.NET Core의 분산 캐싱을 참조하세요. ConfigureServices
의 AddSession에 대한 호출.Configure
의 UseSession에 대한 호출.
다음 코드에서는 IDistributedCache
의 기본 메모리 내 구현을 사용하여 메모리 내 세션 공급자를 설정하는 방법을 보여 줍니다.
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.HttpOnly = true;
options.Cookie.IsEssential = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
}
위의 코드는 테스트를 간소화하기 위해 짧은 시간 제한을 설정합니다.
미들웨어의 순서가 중요합니다. UseRouting
이후, UseEndpoints
이전에 UseSession
을 호출합니다. 미들웨어 순서를 참조하세요.
HttpContext.Session은 세션 상태가 구성된 후에 사용할 수 있습니다.
UseSession
이 호출되기 전에 HttpContext.Session
에 액세스할 수 없습니다.
앱이 응답 스트림에 쓰기 시작한 후에는 새 세션 cookie가 있는 새 세션을 만들 수 없습니다. 예외는 웹 서버 로그에 기록되며 브라우저에는 표시되지 않습니다.
세션 상태를 비동기적으로 로드
ASP.NET Core에서 기본 세션 공급자는 ISession.LoadAsync 메서드가 TryGetValue, Set 또는 Remove 메서드 전에 명시적으로 호출된 경우에만 기본 IDistributedCache 백업 저장소에서 비동기적으로 세션 레코드를 로드합니다. LoadAsync
가 먼저 호출되지 않은 경우 기본 세션 레코드가 동기적으로 로드되며, 이는 크기에 따라 성능 저하를 초래할 수 있습니다.
이 패턴을 앱에 적용하려면 LoadAsync
메서드가 TryGetValue
, Set
또는 Remove
이전에 호출되지 않은 경우 예외를 throw하는 버전으로 DistributedSessionStore 및 DistributedSession 구현을 래핑합니다. 서비스 컨테이너에 래핑된 버전을 등록합니다.
세션 옵션
세션 기본값을 재정의하려면 SessionOptions를 사용합니다.
옵션 | 설명 |
---|---|
Cookie | cookie를 만드는 데 사용되는 설정을 결정합니다. Name 기본값은 SessionDefaults.CookieName(.AspNetCore.Session )입니다. Path 기본값은 SessionDefaults.CookiePath(/ )입니다. SameSite 기본값은 SameSiteMode.Lax(1 )입니다. HttpOnly 기본값은 true 입니다. IsEssential 기본값은 false 입니다. |
IdleTimeout | IdleTimeout 은 콘텐츠가 삭제되기 전까지 세션이 유휴 상태일 수 있는 시간을 나타냅니다. 각 세션 액세스는 시간 제한을 다시 설정합니다. 이 설정은 cookie가 아닌 세션의 콘텐츠에만 적용됩니다. 기본값은 20분입니다. |
IOTimeout | 저장소에서 세션을 로드하거나 저장소로 다시 커밋할 수 있는 최대 시간입니다. 이 설정은 비동기 작업에만 적용될 수 있습니다. 이 시간 제한은 InfiniteTimeSpan을 사용하여 사용하지 않도록 설정할 수 있습니다. 기본값은 1분입니다. |
세션은 cookie를 사용하여 단일 브라우저에서 요청을 추적하고 식별합니다. 기본적으로 이 cookie는 .AspNetCore.Session
이라고 하며 /
의 경로를 사용합니다. cookie 기본값은 도메인을 지정하지 않으므로 페이지에서 클라이언트 쪽 스크립트에 사용할 수 없습니다(HttpOnly 기본값이 true
이므로).
cookie 세션 기본값을 재정의하려면 SessionOptions를 사용합니다.
public void ConfigureServices(IServiceCollection services)
{
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
options.Cookie.Name = ".AdventureWorks.Session";
options.IdleTimeout = TimeSpan.FromSeconds(10);
options.Cookie.IsEssential = true;
});
services.AddControllersWithViews();
services.AddRazorPages();
}
앱은 IdleTimeout 속성을 사용하여 서버 캐시의 콘텐츠가 중단되기 전에 유휴 상태일 수 있는 세션의 기간을 결정합니다. 이 속성은 cookie 만료와 무관합니다. 세션 미들웨어를 통해 전달되는 각 요청은 시간 제한을 다시 설정합니다.
세션 상태는 잠그지 않음입니다. 두 요청이 동시에 세션의 콘텐츠를 수정하려고 하는 경우 마지막 요청이 첫 번째 요청을 재정의합니다. Session
은 일관된 세션으로 구현됩니다. 즉, 모든 콘텐츠는 함께 저장됩니다. 두 요청이 서로 다른 세션 값을 수정하려고 할 때 마지막 요청이 첫 번째 요청에 의해 수행된 세션 변경 내용을 재정의할 수 있습니다.
세션 값 설정 및 가져오기
세션 상태는 HttpContext.Session이 포함된 Razor Pages PageModel 클래스 또는 MVC Controller 클래스에서 액세스됩니다. 이 속성은 ISession 구현입니다.
ISession
구현은 정수 및 문자열 값을 설정 및 검색하는 몇 가지 확장 메서드를 제공합니다. 확장 메서드는 Microsoft.AspNetCore.Http 네임스페이스에 있습니다.
ISession
확장명 메서드:
- Get(ISession, String)
- GetInt32(ISession, String)
- GetString(ISession, String)
- SetInt32(ISession, String, Int32)
- SetString(ISession, String, String)
다음 예제에서는 Razor Pages 페이지에서 IndexModel.SessionKeyName
키(샘플 앱의 _Name
)의 세션 값을 검색합니다.
@page
@using Microsoft.AspNetCore.Http
@model IndexModel
...
Name: @HttpContext.Session.GetString(IndexModel.SessionKeyName)
다음 예제에서는 정수와 문자열을 설정하고 가져오는 방법을 보여 줍니다.
public class IndexModel : PageModel
{
public const string SessionKeyName = "_Name";
public const string SessionKeyAge = "_Age";
const string SessionKeyTime = "_Time";
public string SessionInfo_Name { get; private set; }
public string SessionInfo_Age { get; private set; }
public string SessionInfo_CurrentTime { get; private set; }
public string SessionInfo_SessionTime { get; private set; }
public string SessionInfo_MiddlewareValue { get; private set; }
public void OnGet()
{
// Requires: using Microsoft.AspNetCore.Http;
if (string.IsNullOrEmpty(HttpContext.Session.GetString(SessionKeyName)))
{
HttpContext.Session.SetString(SessionKeyName, "The Doctor");
HttpContext.Session.SetInt32(SessionKeyAge, 773);
}
var name = HttpContext.Session.GetString(SessionKeyName);
var age = HttpContext.Session.GetInt32(SessionKeyAge);
메모리 내 캐시를 사용하는 경우에도, 분산된 캐시 시나리오를 사용하려면 모든 세션 데이터를 직렬화해야 합니다. 문자열 및 정수 직렬 변환기는 ISession의 확장 메서드가 제공합니다. 복합 형식은 JSON과 같은 다른 메커니즘을 사용하여 사용자가 직렬화해야 합니다.
다음 샘플 코드를 사용하여 개체를 직렬화할 수 있습니다.
public static class SessionExtensions
{
public static void Set<T>(this ISession session, string key, T value)
{
session.SetString(key, JsonSerializer.Serialize(value));
}
public static T Get<T>(this ISession session, string key)
{
var value = session.GetString(key);
return value == null ? default : JsonSerializer.Deserialize<T>(value);
}
}
다음 예제는 SessionExtensions
클래스를 사용하여 직렬화 가능 개체를 설정하고 가져오는 방법을 보여 줍니다.
// Requires SessionExtensions from sample download.
if (HttpContext.Session.Get<DateTime>(SessionKeyTime) == default)
{
HttpContext.Session.Set<DateTime>(SessionKeyTime, currentTime);
}
TempData
ASP.NET Core는 Razor Pages TempData 또는 컨트롤러 TempData를 공개합니다. 이 속성은 다른 요청에서 읽혀질 때까지만 데이터를 저장합니다. Keep(문자열) 및 Peek(문자열) 메서드를 사용하면 요청이 끝날 때 삭제하지 않고 데이터를 검사할 수 있습니다. Keep은 사전의 모든 항목을 보존하도록 표시합니다. TempData
인 경우 다음과 같습니다.
- 데이터가 둘 이상의 요청에 필요한 경우 리디렉션에 유용합니다.
TempData
공급자가 쿠키 또는 세션 상태를 사용하여 구현합니다.
TempData 샘플
고객을 만드는 다음 페이지를 살펴보겠습니다.
public class CreateModel : PageModel
{
private readonly RazorPagesContactsContext _context;
public CreateModel(RazorPagesContactsContext context)
{
_context = context;
}
public IActionResult OnGet()
{
return Page();
}
[TempData]
public string Message { get; set; }
[BindProperty]
public Customer Customer { get; set; }
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid)
{
return Page();
}
_context.Customer.Add(Customer);
await _context.SaveChangesAsync();
Message = $"Customer {Customer.Name} added";
return RedirectToPage("./IndexPeek");
}
}
다음 페이지는 TempData["Message"]
를 표시합니다.
@page
@model IndexModel
<h1>Peek Contacts</h1>
@{
if (TempData.Peek("Message") != null)
{
<h3>Message: @TempData.Peek("Message")</h3>
}
}
@*Content removed for brevity.*@
위 태그에서 Peek
가 사용되기 때문에 요청 끝에서 TempData["Message"]
가 삭제되지 않습니다. 페이지를 새로 고치면 TempData["Message"]
의 내용이 표시됩니다.
다음 태그는 위 코드와 비슷하지만 Keep
을 사용하여 요청 끝에서 데이터를 유지합니다.
@page
@model IndexModel
<h1>Contacts Keep</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
TempData.Keep("Message");
}
@*Content removed for brevity.*@
IndexPeek 페이지와 IndexKeep 페이지 사이를 이동해도 TempData["Message"]
가 삭제되지 않습니다.
다음 코드는 TempData["Message"]
를 표시하지만 요청 끝에서 TempData["Message"]
가 삭제됩니다.
@page
@model IndexModel
<h1>Index no Keep or Peek</h1>
@{
if (TempData["Message"] != null)
{
<h3>Message: @TempData["Message"]</h3>
}
}
@*Content removed for brevity.*@
TempData 공급자
cookie-based TempData 공급자는 기본적으로 TempData를 쿠키에 저장하는 데 사용됩니다.
cookie 데이터는 IDataProtector를 사용하여 암호화되고, Base64UrlTextEncoder로 인코딩된 후 청크 분할됩니다. 암호화 및 청크로 인해 최대 cookie 크기는 4,096바이트 미만입니다. cookie 암호화된 데이터를 압축하면 보안 문제(예: CRIME BREACH 공격)가 발생할 수 있으므로 데이터가 압축되지 않습니다. cookie 기반 TempData 공급자에 대한 자세한 내용은 CookieTempDataProvider를 참조하세요.
TempData 공급자 선택
TempData 공급자를 선택하는 데는 다음과 같은 몇 가지 고려 사항이 수반됩니다.
- 앱이 이미 세션 상태를 사용합니까? 그런 경우, 데이터 크기를 제외하고 세션 상태 TempData 공급자 사용과 관련해서 앱에 부과되는 추가 비용은 없습니다.
- 앱이 비교적 적은 양의 데이터(최대 500바이트)에만 TempData를 제한적으로 사용합니까? 그런 경우 cookie TempData 공급자는 TempData를 전달하는 각 요청에 적은 비용을 추가합니다. 그렇지 않은 경우 세션 상태 TempData 공급자는 TempData가 사용될 때까지 각 요청에서 많은 양의 데이터를 왕복 작업하지 않도록 하는 데 도움이 될 수 있습니다.
- 앱이 여러 서버의 서버 팜에서 실행됩니까? 그렇다면 데이터 보호 외부에서 cookie TempData 공급자를 사용하는 데 필요한 추가 구성은 없습니다(ASP.NET Core 데이터 보호 개요 및 키 스토리지 공급자 참조).
웹 브라우저와 같은 대부분의 웹 클라이언트는 각 cookie 쿠키의 최대 크기와 총 쿠키 수에 제한을 적용합니다. cookie TempData 공급자를 사용하는 경우, 앱이 해당 제한을 넘지 않도록 확인합니다. 데이터의 총 크기를 고려합니다. 암호화 및 청크 분할로 인한 cookie 크기 증가를 고려합니다.
TempData 공급자 구성
cookie 기반 TempData 공급자는 기본적으로 활성화됩니다.
세션 기반 TempData 공급자를 사용하도록 설정하려면 AddSessionStateTempDataProvider 확장 메서드를 사용합니다. AddSessionStateTempDataProvider
를 한 번만 호출하면 됩니다.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews()
.AddSessionStateTempDataProvider();
services.AddRazorPages()
.AddSessionStateTempDataProvider();
services.AddSession();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSession();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapRazorPages();
});
}
쿼리 문자열
제한된 양의 데이터는 새 요청의 쿼리 문자열에 추가하여 한 요청에서 다른 요청으로 전달할 수 있습니다. 이는 이메일 또는 소셜 네트워크를 통해 공유되도록 포함된 상태가 있는 링크를 허용하는 영구적인 방식으로 상태를 캡처하는 데 유용합니다. URL 쿼리 문자열은 공용이므로 중요한 데이터에 쿼리 문자열을 사용하지 마세요.
의도하지 않은 공유 외에도, 쿼리 문자열에 데이터를 포함하면 앱이 CSRF(교차 사이트 요청 위조) 공격에 노출될 수 있습니다. 유지된 모든 세션 상태를 CSRF 공격으로부터 보호해야 합니다. 자세한 내용은 ASP.NET Core에서 교차 사이트 요청 위조(XSRF/CSRF) 공격 방지를 참조하세요.
숨겨진 필드
데이터는 숨겨진 양식 필드에 저장되고 다음 요청에서 다시 게시될 수 있습니다. 이는 다중 페이지 폼에서 일반적입니다. 클라이언트는 잠재적으로 데이터를 변조할 수 있으므로 앱은 항상 숨겨진 필드에 저장된 데이터의 유효성을 다시 검사해야 합니다.
HttpContext.Items
HttpContext.Items 컬렉션은 단일 요청을 처리하는 동안 데이터를 저장하는 데 사용됩니다. 컬렉션의 콘텐츠는 요청이 처리된 후 삭제됩니다. Items
컬렉션은 구성 요소 또는 미들웨어가 요청 중에 다른 시점에서 작동하고 매개 변수를 전달할 직접적인 방법이 없는 경우에 통신을 지원하기 위해 자주 사용됩니다.
다음 예제에서 미들웨어는 isVerified
를 Items
컬렉션에 추가합니다.
public void Configure(IApplicationBuilder app, ILogger<Startup> logger)
{
app.UseRouting();
app.Use(async (context, next) =>
{
logger.LogInformation($"Before setting: Verified: {context.Items["isVerified"]}");
context.Items["isVerified"] = true;
await next.Invoke();
});
app.Use(async (context, next) =>
{
logger.LogInformation($"Next: Verified: {context.Items["isVerified"]}");
await next.Invoke();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/", async context =>
{
await context.Response.WriteAsync($"Verified: {context.Items["isVerified"]}");
});
});
}
단일 앱에서만 사용되는 미들웨어의 경우 고정된 string
키가 허용됩니다. 앱 간에 공유되는 미들웨어는 키 충돌을 방지하기 위해 고유한 개체 키를 사용해야 합니다. 다음 예제에서는 미들웨어 클래스에 정의된 고유한 개체 키를 사용하는 방법을 보여 줍니다.
public class HttpContextItemsMiddleware
{
private readonly RequestDelegate _next;
public static readonly object HttpContextItemsMiddlewareKey = new Object();
public HttpContextItemsMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext httpContext)
{
httpContext.Items[HttpContextItemsMiddlewareKey] = "K-9";
await _next(httpContext);
}
}
public static class HttpContextItemsMiddlewareExtensions
{
public static IApplicationBuilder
UseHttpContextItemsMiddleware(this IApplicationBuilder app)
{
return app.UseMiddleware<HttpContextItemsMiddleware>();
}
}
다른 코드는 미들웨어 클래스에 의해 노출된 키를 사용하여 HttpContext.Items
에 저장된 값에 액세스할 수 있습니다.
HttpContext.Items
.TryGetValue(HttpContextItemsMiddleware.HttpContextItemsMiddlewareKey,
out var middlewareSetValue);
SessionInfo_MiddlewareValue =
middlewareSetValue?.ToString() ?? "Middleware value not set!";
이 방법은 또한 코드에서 키 문자열을 사용하지 않아도 된다는 이점이 있습니다.
캐시
캐싱은 데이터 저장 및 검색하는 효율적인 방법입니다. 앱은 캐시된 항목의 수명을 제어할 수 있습니다. 자세한 내용은 ASP.NET Core의 응답 캐싱을 참조하세요.
캐시된 데이터는 특정 요청, 사용자 또는 세션과 연관되지 않습니다. 다른 사용자 요청을 통해 검색될 수 있는 사용자별 데이터를 캐시하면 안 됩니다.
애플리케이션 전반의 데이터를 캐시하려면 ASP.NET Core 메모리 내 캐시를 참조하세요.
일반 오류
"'Microsoft.AspNetCore.Session.DistributedSessionStore'를 활성화하려고 시도하는 동안 'Microsoft.Extensions.Caching.Distributed.IDistributedCache' 형식에 대한 서비스를 확인할 수 없습니다."
이 오류는 일반적으로 하나 이상의
IDistributedCache
구현을 구성하지 못한 경우에 발생합니다. 자세한 내용은 ASP.NET Core의 분산 캐싱 및 ASP.NET Core 메모리 내 캐시를 참조하세요.
세션 미들웨어가 세션을 유지하지 못한 경우
- 미들웨어가 예외를 기록하고 요청은 정상적으로 계속 진행됩니다.
- 이로 인해 예기치 않은 동작이 발생합니다.
백업 저장소를 사용할 수 없는 경우 세션 미들웨어가 세션을 유지하지 못할 수 있습니다. 예를 들어 사용자는 세션에 쇼핑 카트를 저장합니다. 사용자가 카트에 항목을 추가하지만 커밋이 실패합니다. 앱은 실패에 대해 알지 못하므로 항목이 카트에 추가되었다고 보고하지만, 이는 사실이 아닙니다.
오류를 확인하는 권장 방법은 앱이 세션에 기록을 마쳤을 때 await feature.Session.CommitAsync
를 호출하는 것입니다. 백업 저장소를 사용할 수 없는 경우 CommitAsync에서 예외를 throw합니다. CommitAsync
가 실패하면 앱에서 예외를 처리할 수 있습니다. 데이터 저장소를 사용할 수 없는 경우에는 동일한 조건에서 LoadAsync가 throw합니다.
추가 리소스
ASP.NET Core