이 문서에서는 관련 설명서에 대한 링크와 함께 .NET 10의 ASP.NET Core에서 가장 중요한 변경 내용을 강조 표시합니다.
Blazor
이 섹션에서는 Blazor대한 새로운 기능에 대해 설명합니다.
신규 및 업데이트된 Blazor Web App 보안 샘플
다음 문서에 연결된 보안 샘플을 추가하고 업데이트 Blazor Web App 했습니다.
- OpenID Connect (OIDC)로 ASP.NET Core 보안 강화Blazor Web App
- Microsoft Entra ID를 사용하여 ASP.NET Core Blazor Web App 보호
- Windows 인증을 사용하여 ASP.NET Core Blazor Web App 보호
이제 모든 OIDC 및 Entra 샘플 솔루션에는 외부 웹 API를 안전하게 구성하고 호출하는 방법을 보여 주는 별도의 웹 API 프로젝트(MinimalApiJwt)가 포함됩니다. 웹 API 호출은 OIDC ID 공급자 또는 Microsoft Entra ID용 Microsoft Identity 웹 패키지/API에 대한 토큰 처리기 및 명명된 HTTP 클라이언트를 사용하여 보여 줍니다.
샘플 솔루션은 Program 파일 내의 C# 코드로 구성됩니다. 앱 설정 파일(예appsettings.json: )에서 솔루션을 구성하려면 OIDC 또는 Entra 문서의 JSON 구성 공급자(앱 설정) 섹션을 사용하여 새 Supply 구성을 참조하세요.
Entra 문서 및 샘플 앱에는 다음 접근 방식에 대한 새로운 지침도 포함되어 있습니다.
- 웹 팜 호스팅 시나리오에 암호화된 분산 토큰 캐시를 사용하는 방법입니다.
- 데이터 보호를 위해 Azure 관리 ID와 함께 Azure Key Vault를 사용하는 방법입니다.
QuickGrid
RowClass 매개 변수
새 RowClass 매개 변수를 사용하여 행 항목에 따라 표의 행에 스타일시트 클래스를 적용합니다. 다음 예제에서는 각 행에서 GetRowCssClass 메서드를 호출하여 행 항목에 따라 스타일시트 클래스를 조건부로 적용합니다.
<QuickGrid ... RowClass="GetRowCssClass">
...
</QuickGrid>
@code {
private string GetRowCssClass(MyGridItem item) =>
item.IsArchived ? "row-archived" : null;
}
자세한 내용은 ASP.NET Core Blazor 'QuickGrid' 구성 요소참조하세요.
Blazor 스크립트를 정적 웹 자산으로
.NET의 이전 릴리스에서 Blazor 스크립트는 ASP.NET Core 공유 프레임워크의 포함된 리소스에서 제공됩니다. .NET 10 이상에서 Blazor 스크립트는 자동 압축 및 지문을 사용하는 정적 웹 자산으로 제공됩니다.
Blazor 프로젝트에 하나 blazor.web.js 이상의 구성 요소 파일()이 포함된 경우 스크립트(blazor.server.jsRazor또는.razor)가 프레임워크에 포함됩니다. 앱에 스크립트가 Blazor 필요하지만 구성 요소가 하나 이상 포함되지 않은 경우 앱의 프로젝트 파일에 다음 MSBuild 속성을 추가하여 무조건적인 스크립트 포함을 강제합니다.
<RequiresAspNetWebAssets>true</RequiresAspNetWebAssets>
자세한 내용은 다음 리소스를 참조하세요.
경로 템플릿 강조 표시
이제 [Route] 특성 경로 구문 강조 표시를 지원하여 경로 템플릿의 구조를 시각화합니다.
카운터 값에 대한 경로 특성의
강조 표시된 구문을 보여 줍니다.
NavigateTo 더 이상 동일한 페이지 탐색을 위해 위쪽으로 스크롤하지 않습니다.
이전에는 NavigationManager.NavigateTo 동일한 페이지 탐색을 위해 페이지 맨 위로 스크롤했습니다. 이 동작은 .NET 10에서 변경되어 동일한 페이지로 이동할 때 브라우저가 더 이상 페이지 맨 위로 스크롤되지 않도록 합니다. 즉, 쿼리 문자열 또는 조각 변경과 같이 현재 페이지의 주소를 업데이트할 때 뷰포트가 더 이상 다시 설정되지 않습니다.
Blazor Web App 프로젝트 템플릿에 추가된 다시 연결 UI 구성 요소
이제 Blazor Web App 프로젝트 템플릿에는 클라이언트가 서버에 대한 WebSocket 연결을 끊을 때 다시 연결 UI에 대한 개발자 제어를 개선하기 위해 정렬된 스타일시트 및 JavaScript 파일을 비롯한 ReconnectModal 구성 요소가 포함됩니다. 구성 요소는 스타일을 프로그래밍 방식으로 삽입하지 않으므로 style-src 정책에 대해 더 엄격한 CSP(콘텐츠 보안 정책) 설정을 준수합니다. 이전 릴리스에서는 CSP 위반을 일으킬 수 있는 방식으로 프레임워크에서 기본 다시 연결 UI를 만들었습니다. 기본 다시 연결 UI는 앱이 프로젝트 템플릿의 ReconnectModal 구성 요소 또는 유사한 사용자 지정 구성 요소를 사용하는 등 다시 연결 UI를 정의하지 않는 경우에도 대체(fallback)로 사용됩니다.
새로운 다시 연결 UI 기능:
- 다시 연결 UI 요소에서 특정 CSS 클래스를 설정하여 다시 연결 상태를 나타내는 것 외에도 다시 연결 상태 변경을 위해 새
components-reconnect-state-changed이벤트가 디스패치됩니다. - 코드는 CSS 클래스와 새 이벤트 둘 다에서 나타내는 새 다시 연결 상태 "
retrying"로 다시 연결 프로세스의 단계를 더 잘 구분할 수 있습니다.
자세한 내용은 ASP.NET Core BlazorSignalR 지침을 참조하세요.
NavLinkMatch.All 사용할 때 쿼리 문자열 및 조각 무시
이제 NavLink 구성 요소는 NavLinkMatch.All 매개 변수에 Match 값을 사용할 때 쿼리 문자열 및 조각을 무시합니다. 즉, URL 경로가 일치하지만 쿼리 문자열 또는 조각이 변경되는 경우 링크는 active 클래스를 유지합니다. 원래 동작으로 되돌리려면 스위치를 Microsoft.AspNetCore.Components.Routing.NavLink.EnableMatchAllForQueryStringAndFragmentAppContext 다음으로 true설정합니다.
ShouldMatch 메서드를 NavLink에서 재정의하여 일치하는 동작을 사용자 지정할 수도 있습니다.
public class CustomNavLink : NavLink
{
protected override bool ShouldMatch(string currentUriAbsolute)
{
// Custom matching logic
}
}
자세한 내용은 ASP.NET Core Blazor 탐색을 참조하세요.
열 옵션을 닫기 QuickGrid
이제 새 QuickGrid 메서드를 사용하여 HideColumnOptionsAsync 열 옵션 UI를 닫을 수 있습니다.
다음 예제에서는 HideColumnOptionsAsync 메서드를 사용하여 제목 필터가 적용되는 즉시 열 옵션 UI를 닫습니다.
<QuickGrid @ref="movieGrid" Items="movies">
<PropertyColumn Property="@(m => m.Title)" Title="Title">
<ColumnOptions>
<input type="search" @bind="titleFilter" placeholder="Filter by title"
@bind:after="@(() => movieGrid.HideColumnOptionsAsync())" />
</ColumnOptions>
</PropertyColumn>
<PropertyColumn Property="@(m => m.Genre)" Title="Genre" />
<PropertyColumn Property="@(m => m.ReleaseYear)" Title="Release Year" />
</QuickGrid>
@code {
private QuickGrid<Movie>? movieGrid;
private string titleFilter = string.Empty;
private IQueryable<Movie> movies = new List<Movie> { ... }.AsQueryable();
private IQueryable<Movie> filteredMovies =>
movies.Where(m => m.Title!.Contains(titleFilter));
}
기본값으로 설정된 HttpClient 응답 스트리밍 기능 활성화
이전 Blazor 릴리스에서는 요청에 대한 HttpClient 응답 스트리밍이 옵트인되었습니다. 이제 응답 스트리밍은 기본적으로 사용하도록 설정됩니다.
이는 HttpContent.ReadAsStreamAsync의 HttpResponseMessage.Content 호출이 response.Content.ReadAsStreamAsync()에 대해 더 이상 BrowserHttpReadStream가 아닌 MemoryStream를 반환하기 때문에 호환성을 손상하는 변경 사항입니다.
BrowserHttpReadStream 는 와 같은 Stream.Read(Span<Byte>)동기 작업을 지원하지 않습니다. 코드에서 동기 작업을 사용하는 경우, 응답 스트리밍을 선택 해제하거나 Stream를 MemoryStream로 직접 복사할 수 있습니다.
전 세계적으로 응답 스트리밍을 옵트아웃하려면 다음 방법 중 하나를 사용합니다.
프로젝트 파일에
<WasmEnableStreamingResponse>속성을 값false로 추가하십시오.<WasmEnableStreamingResponse>false</WasmEnableStreamingResponse>환경 변수를
DOTNET_WASM_ENABLE_STREAMING_RESPONSE또는false로 설정합니다.
개별 요청에 대한 응답 스트리밍을 옵트아웃하려면, 다음 예제에서 SetBrowserResponseStreamingEnabled의 false에 대해 HttpRequestMessage을(를) requestMessage로 설정하십시오.
requestMessage.SetBrowserResponseStreamingEnabled(false);
자세한 내용을 보려면 Fetch API 요청 옵션(HttpClient 문서)을 HttpRequestMessage및 참조하세요.
클라이언트 측 디바이스 핑거프린팅
.NET 9 릴리스에서는 정적 자산의 서버 측 지문을 도입했고, Blazor Web App, MapStaticAssets, 그리고 지문 JavaScript() 모듈을 해결하기 위한 ImportMap을 포함했습니다. .NET 10의 경우 독립 실행형 JS 앱용 모듈의 Blazor WebAssembly 클라이언트 쪽 지문을 옵트인할 수 있습니다.
빌드 및 게시 중 독립 실행형 Blazor WebAssembly 앱에서 프레임워크는 index.html에 있는 자리 표시자를 빌드 중 계산된 값으로 교체하여 정적 자산에 지문을 생성합니다. 지문 정보는 스크립트 파일 이름에 blazor.webassembly.js 삽입됩니다.
지문 기능을 채택하려면 wwwroot/index.html 파일에 다음 마크업이 있어야 합니다.
<head>
...
+ <script type="importmap"></script>
</head>
<body>
...
- <script src="_framework/blazor.webassembly.js"></script>
+ <script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
</body>
</html>
프로젝트 파일(.csproj)에 속성을 추가하고, <OverrideHtmlAssetPlaceholders>을(를) true로 설정합니다.
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
+ <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
</PropertyGroup>
</Project>
다음 예제에서 JS 개발자가 제공한 모든 파일은 .js 파일 확장자를 사용하는 모듈입니다.
앱의 scripts.js 폴더에 있는 이름이 wwwroot/js인 모듈은 파일 확장명(#[.{fingerprint}]) 앞에 .js를 추가하여 지문을 생성합니다.
<script type="module" src="js/scripts#[.{fingerprint}].js"></script>
앱의 프로젝트 파일(<StaticWebAssetFingerprintPattern>)에서 .csproj 속성을 사용하여 지문 식을 명시하십시오.
<ItemGroup>
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.js"
Expression="#[.{fingerprint}]!" />
</ItemGroup>
지문 마커가 있는 JS 모든 *.js 파일(index.html)은 앱이 게시된 시기를 포함하여 프레임워크에서 지문으로 인식됩니다.
모듈에 .mjs 대한 JS 파일 확장자를 채택하는 경우, Pattern 매개변수를 사용하여 파일 확장자를 설정합니다.
<ItemGroup>
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs"
Expression="#[.{fingerprint}]!" />
</ItemGroup>
파일은 가져오기 맵에 배치됩니다.
- CSR(클라이언트 쪽 렌더링)에 자동으로 Blazor Web App 사용됩니다.
- 이전 지침에 따라 독립 실행형 Blazor WebAssembly 앱에서 모듈 지문 기능을 활성화할 경우
JavaScript interop에 대한 가져오기에서 지문 파일을 해결하기 위해 브라우저가 가져오기 맵을 사용합니다.
미리 로드된 Blazor 프레임워크 정적 자산
프레임워크 Blazor Web App정적 자산은 헤더를 사용하여 Link자동으로 미리 로드되므로 초기 페이지를 가져오고 렌더링하기 전에 브라우저에서 리소스를 미리 로드할 수 있습니다.
독립 실행형 Blazor WebAssembly 앱에서 프레임워크 자산은 다음과 같은 경우 브라우저 index.html 페이지 처리 초기에 우선 순위가 높은 다운로드 및 캐싱을 위해 예약됩니다.
OverrideHtmlAssetPlaceholders앱의 프로젝트 파일(.csproj)에 있는 MSBuild 속성은 다음으로true설정됩니다.<PropertyGroup> <OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders> </PropertyGroup>다음
<link>요소에는rel="preload"이(가) 포함되어 있으며, 이는<head>의wwwroot/index.html콘텐츠에 있습니다.<link rel="preload" id="webassembly" />
자세한 내용은 ASP.NET Core Blazor 정적 파일참조하세요.
독립 실행형 Blazor WebAssembly 앱에서 환경 설정
이 Properties/launchSettings.json 파일은 독립 실행형 앱의 환경을 제어하는 데 더 이상 사용되지 않습니다 Blazor WebAssembly .
.NET 10부터 앱의 프로젝트 파일(<WasmApplicationEnvironmentName>)에서 .csproj 속성을 사용하여 환경을 설정합니다.
다음 예제에서는 앱의 환경을 다음으로 Staging설정합니다.
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
기본 환경은 다음과 같습니다.
-
Development빌드용입니다. -
Production게시용입니다.
자세한 내용은 ASP.NET Core Blazor 환경을 참조하세요.
인라인된 부팅 구성 파일
Blazor의 부팅 구성은 .NET 10이 릴리스되기 전에 blazor.boot.json라는 파일에 있었으나, 이제 dotnet.js 스크립트로 인라인되었습니다. 이는 개발자가 다음과 같은 경우와 같이 파일과 blazor.boot.json 직접 상호 작용하는 개발자에게만 영향을 줍니다.
- ASP.NET Core Blazor WebAssembly 캐싱 및 무결성 검사 실패의 지침에 따라 문제 해결 무결성 PowerShell 스크립트를 사용하여 게시된 자산에 대한 파일 무결성을 확인합니다.
- Host의 지침에 따라 기본 웹실 파일 형식을 사용하지 않는 경우 DLL 파일의 파일 이름 확장명을 변경하고 ASP.NET CoreBlazor WebAssembly를 배포합니다.
현재 이전 방법에 대한 문서화된 대체 전략은 없습니다. 앞의 전략 중 하나가 필요한 경우 두 문서의 맨 아래에 있는 설명서 열기 문제 링크를 사용하여 시나리오를 설명하는 새 설명서 문제를 엽니다.
구성 요소 및 서비스에서 상태를 유지하기 위한 선언적 모델
이제 특성을 사용하여 [PersistentState] 구성 요소 및 서비스에서 유지할 상태를 선언적으로 지정할 수 있습니다. 이 특성이 있는 속성은 미리 렌더링하는 동안 서비스를 사용하여 PersistentComponentState 자동으로 유지됩니다. 구성 요소가 대화형으로 렌더링되거나 서비스가 인스턴스화될 때 상태가 검색됩니다.
이전 Blazor 릴리스에서는 다음 예제와 같이 서비스를 사용하여 PersistentComponentState 미리 렌더링하는 동안 구성 요소 상태를 유지하는 데 상당한 양의 코드가 포함되었습니다.
@page "/movies"
@implements IDisposable
@inject IMovieService MovieService
@inject PersistentComponentState ApplicationState
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
public List<Movie>? MoviesList { get; set; }
private PersistingComponentStateSubscription? persistingSubscription;
protected override async Task OnInitializedAsync()
{
if (!ApplicationState.TryTakeFromJson<List<Movie>>(nameof(MoviesList),
out var movies))
{
MoviesList = await MovieService.GetMoviesAsync();
}
else
{
MoviesList = movies;
}
persistingSubscription = ApplicationState.RegisterOnPersisting(() =>
{
ApplicationState.PersistAsJson(nameof(MoviesList), MoviesList);
return Task.CompletedTask;
});
}
public void Dispose() => persistingSubscription?.Dispose();
}
이제 새 선언적 모델을 사용하여 이 코드를 간소화할 수 있습니다.
@page "/movies"
@inject IMovieService MovieService
@if (MoviesList == null)
{
<p><em>Loading...</em></p>
}
else
{
<QuickGrid Items="MoviesList.AsQueryable()">
...
</QuickGrid>
}
@code {
[PersistentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
상태는 동일한 형식의 여러 구성 요소에 대해 직렬화할 수 있으며, 사용자 지정 서비스 유형 및 렌더링 모드로 구성 요소 작성기(RegisterPersistentService)를 호출 RazorAddRazorComponents 하여 앱에서 사용할 서비스에서 선언적 상태를 설정할 수 있습니다. 자세한 내용은 ASP.NET Core Blazor 미리 렌더링된 상태 지속성을 참조하세요.
새 JavaScript interop 기능
Blazor 는 다음 JS interop 기능에 대한 지원을 추가합니다.
- 생성자 함수를 사용하여 JS 개체 인스턴스를 만들고 인스턴스를 참조하기 위해 IJSObjectReference/IJSInProcessObjectReference .NET 핸들을 가져옵니다.
- JS 개체 속성의 데이터 속성 및 접근자 속성 값을 읽거나 수정합니다.
다음 비동기 메서드는 기존 IJSRuntime 메서드와 동일한 범위 지정 동작을 가진 IJSObjectReference 및 IJSRuntime.InvokeAsync에서 사용할 수 있습니다.
InvokeConstructorAsync(string identifier, object?[]? args): 지정된 생성자 함수를 JS 비동기적으로 호출합니다. 함수는 연산자를 사용하여 호출됩니다new. 다음 예제jsInterop.TestClass에서는 생성자 함수가 있는 클래스이며classRef다음과 같습니다 IJSObjectReference.var classRef = await JSRuntime.InvokeConstructorAsync("jsInterop.TestClass", "Blazor!"); var text = await classRef.GetValueAsync<string>("text"); var textLength = await classRef.InvokeAsync<int>("getTextLength");GetValueAsync<TValue>(string identifier): 지정된 JS 속성의 값을 비동기적으로 읽습니다.set전용 속성이 될 수 없습니다. 속성이 없으면 A JSException 가 throw됩니다. 다음 예제에서는 데이터 속성에서 값을 반환합니다.var valueFromDataPropertyAsync = await JSRuntime.GetValueAsync<int>( "jsInterop.testObject.num");SetValueAsync<TValue>(string identifier, TValue value): 지정된 JS 속성의 값을 비동기적으로 업데이트합니다.get전용 속성이 될 수 없습니다. 대상 개체에 속성이 정의되어 있지 않으면 속성이 만들어집니다. 속성이 있지만 쓸 수 없거나 개체에 새 속성을 추가할 수 없는 경우 A JSException 가 throw됩니다. 다음 예제에서는num에testObject이(가) 존재하지 않으면 값 30으로testObject을(를) 생성합니다.await JSRuntime.SetValueAsync("jsInterop.testObject.num", 30);
이전 메서드 각각에 대해 CancellationToken 인수 또는 TimeSpan 시간 제한 인수를 사용하는 오버로드가 제공됩니다.
다음 동기 메서드들은 기존 IJSInProcessRuntime 메서드와 동일한 범위 지정 동작으로 IJSInProcessObjectReference 및 IJSInProcessObjectReference.Invoke에서 사용할 수 있습니다.
InvokeConstructor(string identifier, object?[]? args): 지정된 생성자 함수를 JS 동기적으로 호출합니다. 함수는 연산자를 사용하여 호출됩니다new. 다음 예제jsInterop.TestClass에서는 생성자 함수가 있는 클래스이며classRef다음과 같습니다 IJSInProcessObjectReference.var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var classRef = inProcRuntime.InvokeConstructor("jsInterop.TestClass", "Blazor!"); var text = classRef.GetValue<string>("text"); var textLength = classRef.Invoke<int>("getTextLength");GetValue<TValue>(string identifier): 지정된 JS 속성의 값을 동기적으로 읽습니다.set전용 속성이 될 수 없습니다. 속성이 없으면 A JSException 가 throw됩니다. 다음 예제에서는 데이터 속성에서 값을 반환합니다.var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var valueFromDataProperty = inProcRuntime.GetValue<int>( "jsInterop.testObject.num");SetValue<TValue>(string identifier, TValue value): 지정된 JS 속성의 값을 동기적으로 업데이트합니다.get전용 속성이 될 수 없습니다. 대상 개체에 속성이 정의되어 있지 않으면 속성이 만들어집니다. 속성이 있지만 쓸 수 없거나 개체에 새 속성을 추가할 수 없는 경우 A JSException 가 throw됩니다. 다음 예제에서는num가 없을 경우,testObject가 값이 20인 상태로 생성됩니다.var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); inProcRuntime.SetValue("jsInterop.testObject.num", 20);
자세한 내용은 .NET 메서드 문서에서 JavaScript 함수 호출 문서의 다음 섹션을 참조하세요.
Blazor WebAssembly 성능 프로파일링 및 진단 카운터
Blazor WebAssembly 앱에 새로운 성능 프로파일링 및 진단 카운터가 제공됩니다. 자세한 내용은 다음 문서를 참조하세요.
를 사용하여 정적 서버 쪽 렌더링 중 방지 NavigationException 하도록 옵트인 NavigationManager.NavigateTo
정적 서버 쪽 렌더링 중 호출 NavigationManager.NavigateTo (정적 SSR)은 NavigationException리디렉션 응답으로 변환되기 전에 실행을 중단합니다. 이로 인해 디버깅 중에 혼동이 발생할 수 있으며, 코드가 NavigateTo 정상적으로 계속 실행되는 대화형 렌더링 동작과 일치하지 않습니다.
.NET 10에서는 정적 SSR 중에 예외가 throw되는 것을 방지하기 위해 MSBuild 속성을 <BlazorDisableThrowNavigationException> 앱의 프로젝트 파일로 설정할 true 수 있습니다.
<PropertyGroup>
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
</PropertyGroup>
MSBuild 속성이 설정된 경우 정적 SSR 중에 호출 NavigationManager.NavigateTo 하면 더 이상 .NavigationException 대신 예외를 발생시키지 않고 탐색을 수행하여 대화형 렌더링에 맞춰 일관되게 작동합니다. 리디렉션이 발생하기 전에 실행된 후 NavigationManager.NavigateTo 의 코드입니다.
.NET 10 Blazor Web App 프로젝트 템플릿은 기본적으로 MSBuild 속성을 true 설정합니다. .NET 10으로 업데이트되는 앱은 새 MSBuild 속성을 사용하고 이전 동작을 방지하는 것이 좋습니다.
MSBuild 속성을 사용하는 경우 throw되는 데 의존하는 NavigationException 코드를 업데이트해야 합니다. .NET 10 Blazor 이 릴리스되기 전에 프로젝트 템플릿의 Identity 기본 Blazor Web AppIdentityRedirectManager UI에서 호출 InvalidOperationException 후 throw RedirectTo 되어 대화형 렌더링 중에 메서드가 호출되지 않았는지 확인합니다. 이제 MSBuild 속성을 사용할 때 이 예외와 [DoesNotReturn] 특성을 제거해야 합니다. 자세한 내용은 .NET 9의 ASP.NET Core에서 .NET 10의 ASP.NET Core로 마이그레이션을 참조하세요.
Blazor라우터에 매개 변수가 있습니다.NotFoundPage
Blazor 이제 존재하지 않는 페이지로 이동할 때 "찾을 수 없음" 페이지를 표시하는 향상된 방법을 제공합니다.
NavigationManager.NotFound 매개 변수를 사용하여 Router 구성 요소에 페이지 형식을 전달함으로써 NotFoundPage가 호출될 때 렌더링할 페이지를 지정할 수 있습니다. 이 기능은 라우팅을 지원하고, 상태 코드 페이지 재실행 미들웨어에서도 작동하며, Blazor가 없는 시나리오와도 호환됩니다.
NotFound렌더 조각(<NotFound>...</NotFound>)은 .NET 10 이상에서 지원되지 않습니다.
<Router AppAssembly="@typeof(Program).Assembly" NotFoundPage="typeof(Pages.NotFound)">
<Found Context="routeData">
<RouteView RouteData="@routeData" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
<NotFound>This content is ignored because NotFoundPage is defined.</NotFound>
</Router>
Blazor 프로젝트 템플릿에는 이제 기본적으로 NotFound.razor 페이지가 포함됩니다. 이 페이지는 앱에서 호출할 때마다 NotFound 자동으로 렌더링되므로 일관된 사용자 환경으로 누락된 경로를 보다 쉽게 처리할 수 있습니다.
자세한 내용은 ASP.NET Core Blazor 탐색을 참조하세요.
정적 SSR 및 전역 대화형 렌더링에 사용되는 NavigationManager 응답을 찾을 수 없음
NavigationManager 이제 정적 서버 쪽 렌더링(정적 SSR) 또는 전역 대화형 렌더링 중에 요청된 리소스를 찾을 수 없는 시나리오를 처리하는 메서드가 포함되어 NotFound 있습니다.
정적 서버 쪽 렌더링(정적 SSR): 호출 NotFound 은 HTTP 상태 코드를 404로 설정합니다.
대화형 렌더링: 라우터(Blazor)에게 찾을 수 없는 콘텐츠를 렌더링하도록 신호를
Router보냅니다.스트리밍 렌더링: 향상된 탐색 이 활성화된 경우 스트리밍 렌더링 은 페이지를 다시 로드하지 않고도 찾을 수 없는 콘텐츠를 렌더링합니다. 향상된 탐색이 차단되면 프레임워크는 페이지 새로 고침을 사용하여 찾을 수 없는 콘텐츠로 리디렉션됩니다.
스트리밍 렌더링은 NotFoundPage 경로가 있는 구성 요소만 렌더링할 수 있으며, 예를 들어 할당(NotFoundPage="...") 또는 상태 코드 페이지 다시 실행 미들웨어 페이지 할당(UseStatusCodePagesWithReExecute)과 같습니다.
DefaultNotFound 404 콘텐츠("Not found일반 텍스트")에는 경로가 없으므로 스트리밍 렌더링 중에 사용할 수 없습니다.
NotFound 콘텐츠 렌더링은 응답이 시작되었는지 여부에 관계없이(순서대로) 다음을 사용합니다.
- 설정된 경우 NotFoundEventArgs.Path 할당된 페이지의 내용을 렌더링합니다.
- 설정된 경우
Router.NotFoundPage할당된 페이지를 렌더링합니다. - 상태 코드 페이지가 구성된 경우 미들웨어 페이지를 다시 실행합니다.
- 이전 방법을 채택하지 않으면 아무 작업도 수행되지 않습니다.
상태 코드 페이지 다시 실행 미들웨어는 브라우저의 주소 표시줄에 잘못된 URL이 입력되거나 앱에 엔드포인트가 없는 링크가 선택되는 경우와 같은 브라우저 기반 주소 라우팅 문제를 우선적으로 처리합니다.
NavigationManager.OnNotFound가 호출될 때 알림을 위해 NotFound 이벤트를 사용할 수 있습니다.
자세한 내용 및 예제는 ASP.NET Core Blazor 탐색을 참조하세요.
라우터가 없는 앱에서 Blazor의 "찾을 수 없음" 응답 지원
사용자 지정 라우터를 구현하는 앱은 NotFound를 사용할 수 있습니다. 렌더러에 호출될 때 NotFound 렌더링해야 하는 페이지를 알리는 두 가지 방법이 있습니다.
응답 상태에 관계없이 작동하는 권장 방법은 호출 UseStatusCodePagesWithReExecute하는 것입니다. NotFound 호출되면 미들웨어는 메서드에 전달된 경로를 렌더링합니다.
app.UseStatusCodePagesWithReExecute(
"/not-found", createScopeForStatusCodePages: true);
UseStatusCodePagesWithReExecute를 사용하지 않더라도 앱은 이미 시작된 응답에 대해 NotFound를 계속 지원할 수 있습니다. 라우터에서 OnNotFoundEvent에 구독하고 NotFoundEventArgs.Path에 'Not Found' 페이지 경로를 할당하여, NotFound가 호출될 때 렌더러에 어떤 콘텐츠를 렌더링할지 알립니다.
CustomRouter.razor:
@using Microsoft.AspNetCore.Components
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Http
@implements IDisposable
@inject NavigationManager NavigationManager
@code {
protected override void OnInitialized() =>
NavigationManager.OnNotFound += OnNotFoundEvent;
[CascadingParameter]
public HttpContext? HttpContext { get; set; }
private void OnNotFoundEvent(object sender, NotFoundEventArgs e)
{
// Only execute the logic if HTTP response has started
// because setting NotFoundEventArgs.Path blocks re-execution
if (HttpContext?.Response.HasStarted == false)
{
return;
}
e.Path = GetNotFoundRoutePath();
}
// Return the path of the Not Found page that you want to display
private string GetNotFoundRoutePath()
{
...
}
public void Dispose() => NavigationManager.OnNotFound -= OnNotFoundEvent;
}
앱에서 두 방법을 모두 사용하는 경우, 처리기에 지정된 '찾지 못한 경로'가 재실행 미들웨어에 구성된 경로보다 우선합니다.
메트릭 및 추적
이 릴리스에서는 앱에 대한 포괄적인 메트릭 및 추적 기능을 도입하여 구성 요소 수명 주기, 탐색, 이벤트 처리 및 회로 관리에 대한 Blazor 자세한 관찰 가능성을 제공합니다.
자세한 내용은 ASP.NET Core Blazor 성능 모범 사례를 참조하세요.
JavaScript 번들러 지원
Blazor빌드 출력은 Gulp, Webpack 및 Rollup과 같은 JavaScript 번들러와 호환되지 않습니다.
Blazor는 이제 WasmBundlerFriendlyBootConfig MSBuild 속성을 true로 설정하여 게시하는 동안 번들러 친화적인 출력을 생성할 수 true 있습니다.
자세한 내용은 ASP.NET Core Blazor 호스트 및 배포를 참조하세요.
Blazor WebAssembly s의 Blazor Web App정적 자산 미리 로드
헤더 <link>를 ResourcePreloader 구성 요소(<ResourcePreloader />)로 교체하여 Blazor Web App 내의 WebAssembly 자산을 미리 로드합니다. 이렇게 하면 앱 기본 경로 구성(<base href="..." />)이 앱의 루트를 올바르게 식별할 수 있습니다.
구성 요소를 제거하면 앱이 콜백을 사용하여 URL을 loadBootResource 수정하는 경우 기능이 비활성화됩니다.
템플릿은 Blazor Web App .NET 10에서 기본적으로 이 기능을 채택하며, .NET 10으로 업그레이드하는 앱은 구성 요소의 헤드 콘텐츠(ResourcePreloader)에 <base> 기본 URL 태그(App) 뒤의 구성 요소를 배치 App.razor 하여 기능을 구현할 수 있습니다.
<head>
...
<base href="/" />
+ <ResourcePreloader />
...
</head>
자세한 내용은 ASP.NET Core 서버 쪽 Blazor 앱 호스트 및 배포를 참조하세요.
향상된 양식 유효성 검사
Blazor 이제 중첩된 개체 및 컬렉션 항목의 속성 유효성 검사를 지원하는 등 양식 유효성 검사 기능이 향상되었습니다.
유효성이 검사된 양식을 만들려면 이전과 마찬가지로 구성 요소 내에서 DataAnnotationsValidator 구성 요소를 사용합니다EditForm.
새 유효성 검사 기능을 옵트인하려면 다음을 수행합니다.
-
AddValidation서비스가 등록된 파일에서Program확장 메서드를 호출합니다. - 구성 요소(Razor)가 아닌 C# 클래스 파일에서 양식 모델 형식을
.razor선언합니다. -
[ValidatableType]특성을 사용하여 루트 폼 모델 타입을 주석으로 처리합니다.
이전 단계를 수행하지 않으면 유효성 검사 동작은 이전 .NET 릴리스와 동일하게 유지됩니다.
다음 예제에서는 향상된 양식 유효성 검사(간결성을 위해 생략된 세부 정보)를 사용하여 고객 주문을 보여 줍니다.
Program.cs에서 서비스 컬렉션의 AddValidation을(를) 호출하여 새로운 유효성 검사 동작을 활성화합니다.
builder.Services.AddValidation();
다음 Order 클래스 [ValidatableType] 에서는 최상위 모델 형식에 특성이 필요합니다. 다른 형식은 자동으로 검색됩니다.
OrderItem와 ShippingAddress는 간결성을 위해 표시되지 않았지만, 이들이 표시된 경우, 중첩 및 컬렉션 유효성 검사는 동일한 방식으로 작동합니다.
Order.cs:
[ValidatableType]
public class Order
{
public Customer Customer { get; set; } = new();
public List<OrderItem> OrderItems { get; set; } = [];
}
public class Customer
{
[Required(ErrorMessage = "Name is required.")]
public string? FullName { get; set; }
[Required(ErrorMessage = "Email is required.")]
public string? Email { get; set; }
public ShippingAddress ShippingAddress { get; set; } = new();
}
다음 OrderPage 구성 요소에는 DataAnnotationsValidator 구성 요소가 EditForm 구성 요소에 있습니다.
OrderPage.razor:
<EditForm Model="Model">
<DataAnnotationsValidator />
<h3>Customer Details</h3>
<div class="mb-3">
<label>
Full Name
<InputText @bind-Value="Model!.Customer.FullName" />
</label>
<ValidationMessage For="@(() => Model!.Customer.FullName)" />
</div>
@* ... form continues ... *@
</EditForm>
@code {
public Order? Model { get; set; }
protected override void OnInitialized() => Model ??= new();
// ... code continues ...
}
구성 요소(Razor파일) 외부에서 .razor 모델 형식을 선언해야 하는 요구 사항은 새 유효성 검사 기능과 Razor 컴파일러 자체가 원본 생성기를 사용하고 있기 때문입니다. 현재 한 원본 생성기의 출력은 다른 원본 생성기의 입력으로 사용할 수 없습니다.
이제 유효성 검사 지원에는 다음이 포함됩니다.
- 이제 중첩된 복합 개체 및 컬렉션의 유효성 검사가 지원됩니다.
- 여기에는 속성 특성, 클래스 특성 및 IValidatableObject 구현으로 정의된 유효성 검사 규칙이 포함됩니다.
- 특성은
[SkipValidation]유효성 검사에서 속성 또는 형식을 제외할 수 있습니다.
- 이제 유효성 검사에서는 리플렉션 기반 구현 대신 원본 생성기 기반 구현을 사용하여 AOT(미리 실행) 컴파일과의 성능 및 호환성을 향상합니다.
DataAnnotationsValidator 이제 구성 요소의 유효성 검사 순서와 단락 동작System.ComponentModel.DataAnnotations.Validator이 동일합니다. 형식 T인스턴스의 유효성을 검사할 때 적용되는 규칙은 다음과 같습니다.
- 중첩된 개체의 재귀적 유효성 검사를 포함하여 멤버 속성의
T유효성이 검사됩니다. - 형식 수준 특성의
T유효성이 검사됩니다. -
IValidatableObject.Validate 구현하는 경우
T메서드가 실행됩니다.
이전 단계 중 하나에서 유효성 검사 오류가 발생하는 경우 나머지 단계는 건너뜁니다.
다른 어셈블리의 유효성 검사 모델 사용
라이브러리 또는 .Client 프로젝트의 Blazor Web App와 같은 다른 어셈블리에 정의된 모델을 사용하여 양식을 유효성 검사할 수 있습니다. 이를 위해, 라이브러리 또는 .Client 프로젝트에서 인스턴스를 인수로 받아 IServiceCollection 메서드를 호출하는 방법을 만듭니다.
- 앱에서 메서드와
AddValidation를 호출하세요.
자세한 내용 및 예제는 ASP.NET Core Blazor 양식 유효성 검사를 참조하세요.
사용자 지정 Blazor 캐시 및 BlazorCacheBootResources MSBuild 속성이 제거됨
이제 모든 Blazor 클라이언트 측 파일이 브라우저에 의해 고유 식별자와 함께 캐시되었으므로, Blazor의 사용자 지정 캐싱 메커니즘 및 BlazorCacheBootResources MSBuild 속성이 프레임워크에서 제거되었습니다. 클라이언트 쪽 프로젝트의 프로젝트 파일에 MSBuild 속성이 포함되어 있으면 더 이상 영향을 주지 않으므로 속성을 제거합니다.
- <BlazorCacheBootResources>...</BlazorCacheBootResources>
자세한 내용은 ASP.NET Core Blazor WebAssembly 캐싱 및 무결성 검사 실패를 참조하세요.
ASP.NET Core에 대한 암호(Web Authentication API) 지원 Identity
웹 인증(WebAuthn) API 지원( Passkeys로 널리 알려짐)은 퍼블릭 키 암호화 및 디바이스 기반 인증을 활용하여 보안 및 사용자 환경을 개선하는 최신 피싱 방지 인증 방법입니다. ASP.NET Core Identity 는 이제 WebAuthn 및 FIDO2 표준을 기반으로 암호 인증을 지원합니다. 이 기능을 사용하면 사용자가 생체 인식 또는 보안 키와 같은 안전한 디바이스 기반 인증 방법을 사용하여 암호 없이 로그인할 수 있습니다.
프로젝트 템플릿은 Blazor Web App 기본 제공 암호 관리 및 로그인 기능을 제공합니다.
자세한 내용은 다음 문서를 참조하세요.
회로 상태 지속성
이제 서버 쪽 렌더링 Blazor Web App중에 전체 페이지 새로 고침이 트리거되지 않는 한 서버에 대한 연결이 장기간 손실되거나 사전에 일시 중지될 때 사용자의 세션(회로) 상태를 유지할 수 있습니다. 이렇게 하면 사용자는 다음 시나리오에서 저장되지 않은 작업을 잃지 않고 세션을 다시 시작할 수 있습니다.
- 브라우저 탭 속도 제어
- 앱을 전환하는 모바일 디바이스 사용자
- 네트워크 중단
- 적극적인 리소스 관리(비활성 회로 일시 중지)
- 향상된 탐색
자세한 내용은 ASP.NET Core Blazor 서버 쪽 상태 관리를 참조하세요.
WebAssembly에서 .NET용 Blazor WebAssembly 핫 리로드
SDK는 WebAssembly 시나리오에 대한 범용 핫 다시 로드 로 마이그레이션되었습니다. 새 MSBuild 속성 WasmEnableHotReload은 기본적으로 핫 다시 로드를 가능하게 하는 true 구성(Debug)을 위해 Configuration == "Debug" 설정되어 있습니다.
사용자 지정 구성 이름이 있는 다른 구성의 경우 핫 다시 로드를 사용하도록 설정하려면 true 앱의 프로젝트 파일에서 값을 설정합니다.
<PropertyGroup>
<WasmEnableHotReload>true</WasmEnableHotReload>
</PropertyGroup>
Debug 구성에 대한 핫 리로드를 사용하지 않으려면 값을 다음과 같이 false로 설정합니다.
<PropertyGroup>
<WasmEnableHotReload>false</WasmEnableHotReload>
</PropertyGroup>
캐싱 문제를 방지하기 위해 PWA 서비스 작업자 등록이 업데이트됨
이제 PWA(프로그레시브 웹 애플리케이션) 프로젝트 템플릿의Blazor 서비스 작업자 등록에 서비스 작업자 업데이트 중 캐싱 문제를 방지하는 옵션이 포함updateViaCache: 'none'됩니다.
- navigator.serviceWorker.register('service-worker.js');
+ navigator.serviceWorker.register('service-worker.js', { updateViaCache: 'none' });
이 옵션은 다음을 보장합니다.
- 브라우저는 캐시된 버전의 서비스 작업자 스크립트를 사용하지 않습니다.
- 서비스 작업자 업데이트는 HTTP 캐싱에 의해 차단되지 않고 안정적으로 적용됩니다.
- PWA 애플리케이션은 서비스 근로자를 더 예측 가능하게 업데이트할 수 있습니다.
이렇게 하면 서비스 작업자 업데이트가 올바르게 적용되지 않도록 할 수 있는 캐싱 문제를 해결합니다. 이는 오프라인 기능을 위해 서비스 작업자를 사용하는 PWA에 특히 중요합니다.
모든 PWA에서 none로 설정된 옵션을 사용하는 것이 좋습니다. 여기에는 .NET 9 이하를 대상으로 하는 PWA도 포함됩니다.
영구 구성 요소 상태에 대한 Serialization 확장성
사용자 지정 직렬 변환기를 PersistentComponentStateSerializer<T>와 함께 구현합니다. 등록된 사용자 지정 serializer가 없으면 serialization은 기존 JSON serialization으로 돌아갑니다.
사용자 지정 serializer는 앱의 Program 파일에 등록됩니다. 다음 예제에서는 CustomUserSerializer 형식에 대해 TUser가 등록됩니다.
builder.Services.AddSingleton<PersistentComponentStateSerializer<TUser>,
CustomUserSerializer>();
사용자 지정 serializer를 사용하여 형식이 자동으로 유지되고 복원됩니다.
[PersistentState]
public User? CurrentUser { get; set; } = new();
OwningComponentBase 이제 IAsyncDisposable을(를) 구현합니다.
OwningComponentBase 이제 리소스 관리를 개선하는 비동기 삭제에 대한 지원이 포함됩니다. 서비스 범위의 동기 및 비동기 삭제를 모두 처리하기 위해 새 메서드 및 업데이트된 DisposeAsync 메서드가 추가되었습니다.
양식에서 숨겨진 입력 필드를 처리하는 새 InputHidden 구성 요소
새 InputHidden 구성 요소는 문자열 값을 저장하기 위한 숨겨진 입력 필드를 제공합니다.
다음 예제에서는 폼의 Parameter 속성에 대한 숨겨진 입력 필드가 만들어집니다. 양식을 제출하면 숨겨진 필드의 값이 표시됩니다.
<EditForm Model="Parameter" OnValidSubmit="Submit" FormName="InputHidden Example">
<InputHidden id="hidden" @bind-Value="Parameter" />
<button type="submit">Submit</button>
</EditForm>
@if (submitted)
{
<p>Hello @Parameter!</p>
}
@code {
private bool submitted;
[SupplyParameterFromForm]
public string Parameter { get; set; } = "stranger";
private void Submit() => submitted = true;
}
향상된 탐색을 위한 영구 구성 요소 상태 지원
Blazor 이제 향상된 탐색 중에 영구 구성 요소 상태 처리를 지원합니다. 향상된 탐색 중에 유지되는 상태는 페이지의 대화형 구성 요소에서 읽을 수 있습니다.
기본적으로 영구 구성 요소 상태는 처음에 페이지에 로드될 때만 대화형 구성 요소에 의해 로드됩니다. 이렇게 하면 구성 요소가 로드된 후 동일한 페이지에 대한 추가 향상된 탐색 이벤트가 발생하는 경우 편집된 웹 폼의 데이터와 같은 중요한 상태를 덮어쓰지 않습니다.
데이터가 읽기 전용이고 자주 변경되지 않는 경우 특성을 설정 AllowUpdates = true[PersistentState]하여 향상된 탐색 중에 업데이트를 허용하도록 옵트인합니다. 이는 페치 비용이 많이 들지만 자주 변경되지 않는 캐시된 데이터를 표시하는 등의 시나리오에 유용합니다. 다음 예제에서는 일기 예보 데이터를 사용하는 AllowUpdates 방법을 보여 줍니다.
[PersistentState(AllowUpdates = true)]
public WeatherForecast[]? Forecasts { get; set; }
protected override async Task OnInitializedAsync()
{
Forecasts ??= await ForecastService.GetForecastAsync();
}
미리 렌더링하는 동안 복원 상태를 건너뛰려면 다음으로 RestoreBehavior설정합니다SkipInitialValue.
[PersistentState(RestoreBehavior = RestoreBehavior.SkipInitialValue)]
public string NoPrerenderedData { get; set; }
다시 연결하는 동안 상태 복원을 건너뛰려면 .로 RestoreBehavior설정합니다SkipLastSnapshot. 다시 연결한 후 새 데이터를 확인하는 데 유용할 수 있습니다.
[PersistentState(RestoreBehavior = RestoreBehavior.SkipLastSnapshot)]
public int CounterNotRestoredOnReconnect { get; set; }
상태를 유지하는 방법을 완전히 제어하는 방법과 PersistentComponentState.RegisterOnRestoring 유사하게 상태를 복원하는 방법을 명령적으로 제어하기 위한 콜백을 등록하기 위한 호출 PersistentComponentState.RegisterOnPersisting 입니다.
Blazor WebAssembly 는 현재 UI 문화권 설정을 따릅니다.
.NET 9 이하에서 독립 실행형 Blazor WebAssembly 앱은 CultureInfo.DefaultThreadCurrentCulture을 기반으로 UI 세계화 리소스를 로드합니다. 정의된 CultureInfo.DefaultThreadCurrentUICulture지역화 문화권에 대한 세계화 데이터를 추가로 로드하려면 앱을 .NET 10 이상으로 업그레이드합니다.
Blazor Hybrid
이 섹션에서는 Blazor Hybrid대한 새로운 기능에 대해 설명합니다.
새로운 .NET MAUIBlazor Hybrid에는 Blazor Web App 및 ASP.NET Core Identity 문서와 샘플이 포함되어 있습니다.
ASP.NET Core .NET MAUI를 사용하여 Blazor Hybrid,Identity 및 웹 애플리케이션에 대한 새 기사 및 샘플 앱이 추가되었습니다.
자세한 내용은 다음 리소스를 참조하세요.
- ASP.NET Core .NET MAUIBlazor HybridIdentity를 사용한 웹 애플리케이션
-
MauiBlazorWebIdentity샘플 앱(dotnet/blazor-samplesGitHub 리포지토리)
SignalR
이 섹션에서는 SignalR대한 새로운 기능에 대해 설명합니다.
최소 API
이 섹션에서는 최소 API에 대한 새로운 기능에 대해 설명합니다.
폼 제출 시 빈 문자열을 nullable 타입에 대해 null로 처리
최소 API에서 복합 객체를 사용할 때 [FromForm] 특성을 사용하면, 양식 전송 시 빈 문자열 값이 null로 변환되며, 이는 구문 분석 오류를 일으키지 않습니다. 이 동작은 최소 API의 복합 개체와 연결되지 않은 양식 게시물에 대한 처리 논리와 일치합니다.
using Microsoft.AspNetCore.Http;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapPost("/todo", ([FromForm] Todo todo) => TypedResults.Ok(todo));
app.Run();
public class Todo
{
public int Id { get; set; }
public DateOnly? DueDate { get; set; } // Empty strings map to `null`
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
이 변화에 기여해 주신 @nvmkpk 감사합니다!
최소 API의 유효성 검사 지원
이제 최소 API의 유효성 검사 지원을 사용할 수 있습니다. 이 기능을 사용하면 API 엔드포인트로 전송된 데이터의 유효성 검사를 요청할 수 있습니다. 유효성 검사를 사용하도록 설정하면 ASP.NET Core 런타임에서 다음 사항에 정의된 유효성 검사를 수행할 수 있습니다.
- Query
- Header
- 요청 메시지 본문
유효성 검사는 네임스페이스의 특성을 DataAnnotations 사용하여 정의됩니다. 개발자는 다음을 통해 유효성 검사 시스템의 동작을 사용자 지정합니다.
- 사용자 지정
[Validation]특성 구현 만들기 -
IValidatableObject복잡한 유효성 검사 논리에 대한 인터페이스 구현
유효성 검사에 실패하면 런타임은 유효성 검사 오류에 대한 세부 정보가 포함된 400 잘못된 요청 응답을 반환합니다.
최소 API에 대한 기본 제공 유효성 검사 지원 사용
애플리케이션의 서비스 컨테이너에 필요한 서비스를 등록하기 위해 확장 메서드를 호출 AddValidation 하여 최소 API에 대한 기본 제공 유효성 검사 지원을 사용하도록 설정합니다.
builder.Services.AddValidation();
이 구현은 최소 API 처리기 또는 최소 API 처리기에 정의된 형식의 기본 형식으로 정의된 형식을 자동으로 검색합니다. 엔드포인트 필터는 이러한 형식에 대한 유효성 검사를 수행하고 각 엔드포인트에 대해 추가됩니다.
다음 예제와 같이 확장 메서드를 사용하여 특정 엔드포인트에 DisableValidation 대해 유효성 검사를 사용하지 않도록 설정할 수 있습니다.
app.MapPost("/products",
([EvenNumber(ErrorMessage = "Product ID must be even")] int productId, [Required] string name)
=> TypedResults.Ok(productId))
.DisableValidation();
Note
.NET 10용 ASP.NET Core에 도입된 최소 API 유효성 검사 생성기에 대한 몇 가지 작은 개선 사항과 수정 사항이 적용되었습니다. 향후 향상된 기능을 지원하기 위해 기본 유효성 검사 확인자 API는 이제 실험적 API로 표시됩니다. 최상위 API AddValidation 및 기본 제공 유효성 검사 필터는 안정적이고 실험적이지 않은 상태로 유지됩니다.
레코드 형식을 사용하여 유효성 검사
최소 API는 C# 레코드 형식의 유효성 검사도 지원합니다. 레코드 형식은 클래스와 유사하게 네임스페이스의 System.ComponentModel.DataAnnotations 특성을 사용하여 유효성을 검사할 수 있습니다. 다음은 그 예입니다.
public record Product(
[Required] string Name,
[Range(1, 1000)] int Quantity);
최소 API 엔드포인트에서 레코드 형식을 매개 변수로 사용하는 경우 유효성 검사 특성은 클래스 형식과 동일한 방식으로 자동으로 적용됩니다.
app.MapPost("/products", (Product product) =>
{
// Endpoint logic here
return TypedResults.Ok(product);
});
IProblemDetailsService와 최소 API 유효성 검사 통합
이제 최소 API에 대한 유효성 검사 논리의 오류 응답을 애플리케이션 서비스 컬렉션(종속성 주입 컨테이너)에 제공된 구현에 의해 IProblemDetailsService 사용자 지정할 수 있습니다. 이렇게 하면 보다 일관되고 사용자별 오류 응답이 가능합니다.
Server-Sent 이벤트에 대한 지원 (SSE)
이제 ASP.NET Core는 TypedResults.ServerSentEvents API를 사용하여 ServerSentEvents 결과 반환을 지원합니다. 이 기능은 최소 API 및 컨트롤러 기반 앱 모두에서 지원됩니다.
Server-Sent 이벤트는 서버가 단일 HTTP 연결을 통해 클라이언트에 이벤트 메시지 스트림을 보낼 수 있도록 하는 서버 푸시 기술입니다. .NET에서 이벤트 메시지는 이벤트 형식, ID 및 형식SseItem<T>의 데이터 페이로드를 포함할 수 있는 개체로 T 표시됩니다.
TypedResults 클래스에는 결과를 반환하는 데 사용할 수 있는 ServerSentEvents라는 새 정적 메서드가 ServerSentEvents 있습니다. 이 메서드의 첫 번째 매개 변수는 클라이언트로 보낼 이벤트 메시지의 스트림을 나타내는 매개 변수입니다 IAsyncEnumerable<SseItem<T>> .
다음 예제에서는 API를 TypedResults.ServerSentEvents 사용하여 심박수 이벤트의 스트림을 클라이언트에 JSON 개체로 반환하는 방법을 보여 줍니다.
app.MapGet("/json-item", (CancellationToken cancellationToken) =>
{
async IAsyncEnumerable<HeartRateRecord> GetHeartRate(
[EnumeratorCancellation] CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
var heartRate = Random.Shared.Next(60, 100);
yield return HeartRateRecord.Create(heartRate);
await Task.Delay(2000, cancellationToken);
}
}
return TypedResults.ServerSentEvents(GetHeartRate(cancellationToken),
eventType: "heartRate");
});
자세한 내용은 다음을 참조하세요.
- Server-Sent 이벤트 on MDN.
- API를 사용하여 심박수 이벤트의 스트림을 문자열로 반환하고 JSON
TypedResults.ServerSentEvents개체를 클라이언트에 반환하는ServerSentEvents입니다. - API를 사용하여 심박수 이벤트의 스트림을 문자열로 반환하고 JSON
TypedResults.ServerSentEvents개체를 클라이언트에 반환하는ServerSentEvents입니다.
유효성 검사 API가 Microsoft.Extensions.Validation으로 이동됨
유효성 검사 API가 네임스페이 Microsoft.Extensions.Validation 스 및 NuGet 패키지로 이동되었습니다. 이렇게 변경하면 ASP.NET Core HTTP 시나리오 외부에서 API를 사용할 수 있습니다. 퍼블릭 API와 동작은 변경되지 않고 패키지와 네임스페이스만 다릅니다. 기존 프로젝트는 이전 참조가 새 구현으로 리디렉션하기 때문에 코드 변경이 필요하지 않습니다.
클래스 및 레코드에 대한 향상된 유효성 검사
이제 일관된 코드 생성 및 유효성 검사 동작을 사용하여 클래스와 레코드 모두에 유효성 검사 특성을 적용할 수 있습니다. 이 향상된 기능은 ASP.NET Core 앱에서 레코드를 사용하여 모델을 디자인할 때 유연성을 향상시킵니다.
커뮤니티 기여: @marcominerva 감사합니다!
OpenAPI
이 섹션에서는 OpenAPI의 새로운 기능에 대해 설명합니다.
OpenAPI 3.1 지원
ASP.NET Core는 .NET 10에서 OpenAPI 버전 3.1 문서를 생성하기 위한 지원을 추가했습니다. 소 버전 업데이트임에도 불구하고, OpenAPI 3.1은 특히 JSON 스키마 초안 2020-12에 대한 완전한 지원을 통해 OpenAPI 사양에 대한 중요한 업데이트입니다.
생성된 OpenAPI 문서에 표시되는 변경 내용 중 일부는 다음과 같습니다.
- Nullable 형식에는 스키마에
nullable: true속성이 더 이상 없습니다. -
nullable: true속성 대신, 값이 배열로 표현되어 있으며 그 배열에는type를 포함하는null키워드가 있습니다. - C#
int또는long로 정의된 속성이나 매개변수는 이제type: integer필드 없이 생성된 OpenAPI 문서에 나타나며,pattern필드가 있어 값을 숫자로 제한합니다. 이 문제는 NumberHandling의 JsonSerializerOptions 속성이AllowReadingFromString로 설정된 경우 발생하는데, 이 설정은 ASP.NET Core 웹 앱의 기본값입니다. OpenAPI 문서에 C#int및long을type: integer로 표현하려면, NumberHandling 속성을Strict로 설정하십시오.
이 기능을 사용하면 생성된 문서에 대한 기본 OpenAPI 버전이3.1.
AddOpenApi의 대리자 매개 변수에서 OpenApiOptions의 OpenApiVersionconfigureOptions 속성을 명시적으로 설정하여 버전을 변경할 수 있습니다.
builder.Services.AddOpenApi(options =>
{
options.OpenApiVersion = Microsoft.OpenApi.OpenApiSpecVersion.OpenApi3_1;
});
빌드 시 OpenAPI 문서를 생성할 때 MSBuild 항목에서 --openapi-version 설정 OpenApiGenerateDocumentsOptions 하여 OpenAPI 버전을 선택할 수 있습니다.
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<OpenApiGenerateDocuments>true</OpenApiGenerateDocuments>
<!-- Configure build-time OpenAPI generation to produce an OpenAPI 3.1 document. -->
<OpenApiGenerateDocumentsOptions>--openapi-version OpenApi3_1</OpenApiGenerateDocumentsOptions>
</PropertyGroup>
OpenAPI 3.1 지원은 주로 PR를 통해 추가되었습니다.
OpenAPI 3.1 호환성에 영향을 미치는 변경 사항
OpenAPI 3.1을 지원하려면 기본 OpenAPI.NET 라이브러리를 새 주 버전인 2.0으로 업데이트해야 합니다. 이 새 버전에는 이전 버전과 비교하여 호환성에 중대한 영향을 미치는 변경 내용이 있습니다. 주요 변경 사항은 문서, 운영, 또는 스키마 변환기가 있는 경우 앱에 영향을 미칠 수 있습니다. 이 반복의 주요 변경 내용은 다음과 같습니다.
- 작업 및 매개 변수와 같은 OpenAPI 문서 내의 엔터티는 인터페이스로 입력됩니다. 엔터티의 인라인 및 참조된 변형에 대한 명확한 구현이 있습니다. 예를 들어,
IOpenApiSchema는 인라인된OpenApiSchema일 수도 있고 문서의 다른 위치에 정의된 스키마를 가리키는OpenApiSchemaReference일 수도 있습니다. -
Nullable속성이OpenApiSchema형식에서 제거되었습니다. 형식이 nullable인지 확인하려면OpenApiSchema.Type속성이JsonSchemaType.Null설정하는지 평가합니다.
가장 중요한 변경 사항 중 하나는 OpenApiAny 클래스가 JsonNode 직접 사용하기 위해 삭제되었다는 것입니다.
OpenApiAny 사용하는 변환기는 JsonNode사용하도록 업데이트해야 합니다. .NET 9에서 .NET 10으로 업그레이드된 스키마 변환기의 변경 내역은 다음과 같습니다.
options.AddSchemaTransformer((schema, context, cancellationToken) =>
{
if (context.JsonTypeInfo.Type == typeof(WeatherForecast))
{
- schema.Example = new OpenApiObject
+ schema.Example = new JsonObject
{
- ["date"] = new OpenApiString(DateTime.Now.AddDays(1).ToString("yyyy-MM-dd")),
+ ["date"] = DateTime.Now.AddDays(1).ToString("yyyy-MM-dd"),
- ["temperatureC"] = new OpenApiInteger(0),
+ ["temperatureC"] = 0,
- ["temperatureF"] = new OpenApiInteger(32),
+ ["temperatureF"] = 32,
- ["summary"] = new OpenApiString("Bracing"),
+ ["summary"] = "Bracing",
};
}
return Task.CompletedTask;
});
이러한 변경은 OpenAPI 버전을 3.0으로 구성하는 경우에만 필요합니다.
YAML의 OpenAPI
이제 ASP.NET 생성된 OpenAPI 문서를 YAML 형식으로 제공합니다. YAML은 JSON보다 간결할 수 있으므로 유추할 수 있을 때 중괄호와 따옴표를 제거합니다. YAML은 긴 설명에 유용할 수 있는 여러 줄 문자열도 지원합니다.
생성된 OpenAPI 문서를 YAML 형식으로 제공하도록 앱을 구성하려면 다음 예제와 같이 ".yaml" 또는 ".yml" 접미사를 사용하여 MapOpenApi 호출의 엔드포인트를 지정합니다.
if (app.Environment.IsDevelopment())
{
app.MapOpenApi("/openapi/{documentName}.yaml");
}
지원 대상:
- YAML은 현재 OpenAPI 엔드포인트에서 제공되는 OpenAPI에만 사용할 수 있습니다.
- 빌드 시 YAML 형식으로 OpenAPI 문서를 생성하는 작업이 향후 미리 보기에 추가됩니다.
생성된 OpenAPI 문서를 YAML 형식으로 제공하는 지원을 추가한 이 PR 참조하세요.
API 컨트롤러에 ProducesResponseType 대한 응답 설명
ProducesAttribute이제 ProducesResponseTypeAttributeProducesDefaultResponseTypeAttribute 응답에 대한 설명을 설정하는 선택적 문자열 매개 변수Description를 허용합니다.
[HttpGet(Name = "GetWeatherForecast")]
[ProducesResponseType<IEnumerable<WeatherForecast>>(StatusCodes.Status200OK,
Description = "The weather forecast for the next 5 days.")]
public IEnumerable<WeatherForecast> Get()
{
생성된 OpenAPI 데이터:
"responses": {
"200": {
"description": "The weather forecast for the next 5 days.",
"content": {
이 기능은 API 컨트롤러와 최소 API 모두에서 지원 됩니다. 최소 API Description 의 경우 특성의 형식과 유추된 반환 형식이 정확히 일치하지 않는 경우에도 속성이 올바르게 설정됩니다.
dotnet/aspnetcore의 커뮤니티 기여(#58193)
OpenAPI 문서에 XML 문서 주석 채우기
ASP.NET Core OpenAPI 문서 생성에는 이제 OpenAPI 문서의 메서드, 클래스 및 멤버 정의에 대한 XML 문서 주석의 메타데이터가 포함됩니다. 이 기능을 사용하려면 프로젝트 파일에서 XML 문서 주석을 사용하도록 설정해야 합니다. 프로젝트 파일에 다음 속성을 추가하여 이 작업을 수행할 수 있습니다.
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
빌드 시 OpenAPI 패키지는 원본 생성기를 활용하여 현재 애플리케이션 어셈블리 및 프로젝트 참조에서 XML 주석을 검색하고 소스 코드를 내보내 OpenAPI 문서 변환기를 통해 문서에 삽입합니다.
C# 빌드 프로세스는 람다 설명에 배치된 XML 문서 주석을 캡처하지 않으므로 XML 문서 주석을 사용하여 최소 API 엔드포인트에 메타데이터를 추가하려면 엔드포인트 처리기를 메서드로 정의하고 메서드에 XML 문서 주석을 배치한 다음 메서드에서 MapXXX 해당 메서드를 참조해야 합니다. 예를 들어 XML 문서 주석을 사용하여 원래 람다 식으로 정의된 최소 API 엔드포인트에 메타데이터를 추가하려면 다음을 수행합니다.
app.MapGet("/hello", (string name) =>$"Hello, {name}!");
메서드를 참조하도록 MapGet 호출을 변경합니다.
app.MapGet("/hello", Hello);
XML 문서 주석을 사용하여 Hello 메서드를 정의합니다.
static partial class Program
{
/// <summary>
/// Sends a greeting.
/// </summary>
/// <remarks>
/// Greeting a person by their name.
/// </remarks>
/// <param name="name">The name of the person to greet.</param>
/// <returns>A greeting.</returns>
public static string Hello(string name)
{
return $"Hello, {name}!";
}
}
이전 예제에서 Hello 메서드는 Program 클래스에 추가되지만 프로젝트의 모든 클래스에 추가할 수 있습니다.
이전 예제에서는 <summary>, <remarks>및 <param> XML 문서 주석을 보여 줍니다.
지원되는 모든 태그를 포함하여 XML 문서 주석에 대한 자세한 내용은 C# 설명서를 참조하세요.
핵심 기능은 원본 생성기를 통해 제공되므로 프로젝트 파일에 다음 MSBuild를 추가하여 사용하지 않도록 설정할 수 있습니다.
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.0-preview.2.*" GeneratePathProperty="true" />
</ItemGroup>
<Target Name="DisableCompileTimeOpenApiXmlGenerator" BeforeTargets="CoreCompile">
<ItemGroup>
<Analyzer Remove="$(PkgMicrosoft_AspNetCore_OpenApi)/analyzers/dotnet/cs/Microsoft.AspNetCore.OpenApi.SourceGenerators.dll" />
</ItemGroup>
</Target>
AdditionalFiles 속성에 포함된 원본 생성기 프로세스 XML 파일입니다. 속성을 추가(또는 제거)하려면 원천이 다음과 같이 속성을 수정합니다.
<Target Name="AddXmlSources" BeforeTargets="CoreCompile">
<ItemGroup>
<AdditionalFiles Include="$(PkgSome_Package)/lib/net10.0/Some.Package.xml" />
</ItemGroup>
</Target>
ASP.NET Core 웹 API(네이티브 AOT) 템플릿에 추가된 Microsoft.AspNetCore.OpenApi
ASP.NET Core AOT(Native AOT) 프로젝트 템플릿(짧은 이름webapiaot)에는 기본적으로 패키지를 사용하는 Microsoft.AspNetCore.OpenApi OpenAPI 문서 생성에 대한 지원이 포함됩니다. 이 지원은 새 프로젝트를 만들 때 플래그를 --no-openapi 사용하여 사용하지 않도록 설정됩니다.
dotnet/aspnetcore의 커뮤니티 기여(#60337)
DI 컨테이너에서 IOpenApiDocumentProvider를 지원합니다.
.NET 10의 ASP.NET Core는 DI(종속성 주입) 컨테이너에서 IOpenApiDocumentProvider 를 지원합니다. 개발자는 앱에 삽입 IOpenApiDocumentProvider 하고 이를 사용하여 OpenAPI 문서에 액세스할 수 있습니다. 이 방법은 백그라운드 서비스 또는 사용자 지정 미들웨어와 같은 HTTP 요청 컨텍스트 외부에서 OpenAPI 문서에 액세스하는 데 유용합니다.
이전에는 no-op 구현을 사용하여 HostFactoryResolver HTTP 서버를 시작하지 않고 애플리케이션 시작 논리를 IServer 실행할 수 있습니다. 새로운 기능은 분산 애플리케이션 호스팅 및 게시를 위한 프레임워크의 Aspire일부인 'sIDistributedApplicationPublisher'에서 영감을 받은 Aspire간소화된 API를 제공하여 이 프로세스를 간소화합니다.
자세한 내용은 dotnet/aspnetcore #61463을 참조하세요.
XML 주석 생성기의 향상된 기능
XML 주석 생성은 .NET 10의 복합 형식을 이전 버전의 .NET보다 더 잘 처리합니다.
- 더 넓은 범위의 형식에 대해 정확하고 완전한 XML 주석을 생성합니다.
- 더 복잡한 시나리오를 처리합니다.
- 이전 버전에서 빌드 오류를 일으키는 복합 형식에 대한 처리를 정상적으로 무시합니다.
이러한 개선 사항은 특정 시나리오의 실패 모드를 빌드 오류에서 누락된 메타데이터로 변경합니다.
또한 이제 다른 어셈블리의 XML 주석에 액세스하도록 XML 문서 주석 처리를 구성할 수 있습니다. 이는 ProblemDetails 네임스페이스의 Microsoft.AspNetCore.Http 형식과 같이 현재 어셈블리 외부에서 정의된 형식에 대한 문서를 생성하는 데 유용합니다.
이 구성은 프로젝트 빌드 파일의 지시문을 사용하여 수행됩니다. 다음 예제에서는 Microsoft.AspNetCore.Http 클래스를 포함하는 어셈블리 ProblemDetails의 형식에 대한 XML 주석에 접근하기 위해 XML 주석 생성기를 구성하는 방법을 보여 줍니다.
<Target Name="AddOpenApiDependencies" AfterTargets="ResolveReferences">
<ItemGroup>
<!-- Include XML documentation from Microsoft.AspNetCore.Http.Abstractions
to get metadata for ProblemDetails -->
<AdditionalFiles
Include="@(ReferencePath->'
%(RootDir)%(Directory)%(Filename).xml')"
Condition="'%(ReferencePath.Filename)' ==
'Microsoft.AspNetCore.Http.Abstractions'"
KeepMetadata="Identity;HintPath" />
</ItemGroup>
</Target>
대부분의 경우 이 구성이 필요하지 않도록 향후 미리 보기에서 공유 프레임워크에서 선택한 어셈블리 집합의 XML 주석을 포함할 것으로 예상됩니다.
OpenAPI XML 주석 생성기에서 설명서 ID의 통합 처리
참조된 어셈블리의 XML 문서 주석은 문서 ID에 반환 형식 접미사가 포함된 경우에도 올바르게 병합됩니다. 따라서 모든 유효한 XML 주석이 생성된 OpenAPI 설명서에 안정적으로 포함되어 참조된 어셈블리를 사용하는 API에 대한 설명서 정확도와 완전성을 향상합니다.
폼 데이터 열거형 매개 변수는 OpenAPI에서 실제 열거형 형식을 사용합니다.
MVC 컨트롤러 작업의 양식 데이터 매개 변수는 이제 문자열을 기본값으로 설정하지 않고 실제 열거형 형식을 사용하여 OpenAPI 메타데이터를 생성합니다.
커뮤니티 기여: @ascott18 감사합니다!
변환기에서 OpenApiSchemas 생성 지원
개발자는 이제 ASP.NET Core OpenAPI 문서 생성과 동일한 논리를 사용하여 C# 형식에 대한 스키마를 생성하고 OpenAPI 문서에 추가할 수 있습니다. 그런 다음, OpenAPI 문서의 다른 위치에서 스키마를 참조할 수 있습니다.
문서, 작업 및 스키마 변환기에 전달되는 컨텍스트에는 형식에 대한 스키마를 생성하는 데 사용할 수 있는 새 GetOrCreateSchemaAsync 메서드가 포함됩니다.
또한 이 메서드에는 생성된 스키마에 대한 추가 메타데이터를 지정하는 선택적 ApiParameterDescription 매개 변수가 있습니다.
OpenAPI 문서에 Document 스키마를 추가할 수 있도록 작업 및 스키마 변환기 컨텍스트에 속성이 추가되었습니다. 이렇게 하면 변환기가 문서의 AddComponent 메서드를 사용하여 OpenAPI 문서에 스키마를 추가할 수 있습니다.
Example
문서, 작업 또는 스키마 변환기에서 이 기능을 사용하려면 컨텍스트에 제공된 메서드를 사용하여 GetOrCreateSchemaAsync 스키마를 만들고 문서의 AddComponent 메서드를 사용하여 OpenAPI 문서에 추가합니다.
builder.Services.AddOpenApi(options =>
{
options.AddOperationTransformer(async (operation, context, cancellationToken) =>
{
// Generate schema for error responses
var errorSchema = await context.GetOrCreateSchemaAsync(typeof(ProblemDetails), null, cancellationToken);
context.Document?.AddComponent("Error", errorSchema);
operation.Responses ??= new OpenApiResponses();
// Add a "4XX" response to the operation with the newly created schema
operation.Responses["4XX"] = new OpenApiResponse
{
Description = "Bad Request",
Content = new Dictionary<string, OpenApiMediaType>
{
["application/problem+json"] = new OpenApiMediaType
{
Schema = new OpenApiSchemaReference("Error", context.Document)
}
}
};
});
});
엔드포인트별 OpenAPI 작업 변환기
엔드포인트별 작업 변환기를 사용하면 개별 경로 엔드포인트에 대해 OpenAPI 설명서를 세밀하게 사용자 지정할 수 있습니다. 이 기능을 사용하면 개발자가 작업별 또는 경로별로 Swagger/OpenAPI 메타데이터 및 설명을 조정하여 고급 API 시나리오에 대한 확장성을 향상시킬 수 있습니다.
구현 세부 정보 및 코드 샘플은 OpenAPI 문서 사용자 지정을 참조하세요.
Microsoft.OpenApi를 2.0.0으로 업그레이드
Microsoft.OpenApi ASP.NET Core에서 OpenAPI 문서 생성에 사용되는 라이브러리가 버전 2.0.0(GA)으로 업그레이드되었습니다.
2.0.0의 호환성이 손상되는 변경
다음 주요 변경 내용은 미리 보기 릴리스에 도입되었으며 GA 버전에 남아 있습니다. 주로 문서, 작업 또는 스키마 변환기를 구현하는 사용자에게 영향을 미칩니다.
GA 버전이 업데이트되면 OpenAPI 문서 생성 시 더 이상 호환성이 손상되는 변경이 필요하지 않습니다.
OpenAPI 스키마 생성 향상
OpenAPI 스키마에서 oneOf를 사용하여 nullable 형식 모델링
복합 형식 및 컬렉션에 대해 nullable 속성 대신 패턴을 사용하여 oneOf nullable 형식에 대한 OpenAPI 스키마 생성이 향상되었습니다. 구현:
- 요청 및 응답 스키마에서 nullable 복합 형식에 대한 실제 형식 스키마와 함께
oneOf사용합니다null. - 리플렉션 및
NullabilityInfoContext을 사용하여 매개변수, 속성 및 반환 형식의 null 허용 여부를 감지합니다. - 중복을 방지하기 위해 구성 요소화된 스키마에서 null 형식을 제거합니다.
스키마 참조 해결의 수정 및 개선 사항
이 릴리스는 루트 스키마 문서에서 상대 JSON 스키마 참조($ref)를 제대로 확인하여 OpenAPI 문서 생성을 위한 JSON 스키마 처리를 개선합니다.
OpenAPI 스키마에서 $ref의 형제 항목으로 속성 설명 포함
.NET 10 이전에는 OpenAPI v3.0에서 스키마 정의와 $ref 함께 $ref 형제 속성을 허용하지 않았기 때문에 ASP.NET Core는 생성된 OpenAPI 문서에 정의된 속성에 대한 설명을 삭제했습니다. 이제 OpenAPI 3.1을 사용하여 설명을 함께 $ref포함할 수 있습니다. RC1은 생성된 OpenAPI 스키마에서 $ref의 형제로 속성 설명을 포함할 수 있도록 지원을 추가합니다.
이것은 지역 사회 기여였습니다. 감사합니다 @desjoerd!
형식에 대한 [AsParameters] XML 주석의 메타데이터를 OpenAPI 스키마에 추가
OpenAPI 스키마 생성은 이제 매개 변수 클래스의 [AsParameters] 속성에 대한 XML 주석을 처리하여 설명서에 대한 메타데이터를 추출합니다.
OpenAPI에서 알 수 없는 HTTP 메서드 제외
OpenAPI 스키마 생성은 이제 생성된 OpenAPI 문서에서 알 수 없는 HTTP 메서드를 제외합니다. 표준 HTTP 메서드이지만 OpenAPI에서 인식되지 않는 쿼리 메서드는 이제 생성된 OpenAPI 문서에서 정상적으로 제외됩니다.
이것은 지역 사회 기여였습니다. 감사합니다 @martincostello!
JSON 패치 요청 본문에 대한 설명 개선
이제 JSON 패치 작업에 대한 OpenAPI 스키마 생성이 미디어 형식을 application/json-patch+json JSON 패치를 사용하는 요청 본문에 올바르게 적용합니다. 이렇게 하면 생성된 OpenAPI 문서가 JSON 패치 작업에 필요한 미디어 형식을 정확하게 반영합니다. 또한 JSON 패치 요청 본문에는 수행할 수 있는 작업을 포함하여 JSON 패치 문서의 구조를 설명하는 자세한 스키마가 있습니다.
이것은 지역 사회 기여였습니다. 감사합니다 @martincostello!
OpenAPI 문서 생성을 위해 불변 문화권 사용
이제 OpenAPI 문서 생성을 위해 생성된 OpenAPI 문서에서 숫자와 날짜를 포맷팅할 때 불변 문화 설정을 사용합니다. 이렇게 하면 생성된 문서가 일관되고 서버의 문화권 설정에 따라 달라지지 않습니다.
이것은 지역 사회 기여였습니다. 감사합니다 @martincostello!
인증 및 권한 부여
인증 및 권한 부여 메트릭
ASP.NET Core의 특정 인증 및 권한 부여 이벤트에 대한 메트릭이 추가되었습니다. 이 변경으로 이제 다음 이벤트에 대한 메트릭을 가져올 수 있습니다.
- Authentication:
- 인증된 요청 기간
- 도전 횟수
- 카운트 금지
- 로그인 수
- 로그아웃 횟수
- Authorization:
- 권한 부여가 필요한 요청 수
다음 이미지는 대시보드에서 인증된 요청 기간 메트릭 Aspire 의 예를 보여줍니다.
자세한 내용은 ASP.NET Core 기본 제공 메트릭을 참조하세요.
Identity 핵심 메트릭 ASP.NET
ASP.NET Core Identity .NET 10에서 메트릭을 사용하여 관찰 가능성이 향상되었습니다. 메트릭은 시스템 또는 애플리케이션 동작의 시계열 측정을 제공하는 카운터, 히스토그램 및 계기입니다.
예를 들어 새 ASP.NET Core Identity 메트릭을 사용하여 다음을 관찰합니다.
- 사용자 관리: 새 사용자 만들기, 암호 변경 및 역할 할당
- 로그인/세션 처리: 2단계 인증을 사용하여 로그인 시도, 로그인, 로그아웃 및 사용자입니다.
새 메트릭은 미터에 Microsoft.AspNetCore.Identity 있습니다.
aspnetcore.identity.user.create.durationaspnetcore.identity.user.update.durationaspnetcore.identity.user.delete.durationaspnetcore.identity.user.check_password_attemptsaspnetcore.identity.user.generated_tokensaspnetcore.identity.user.verify_token_attemptsaspnetcore.identity.sign_in.authenticate.durationaspnetcore.identity.sign_in.check_password_attemptsaspnetcore.identity.sign_in.sign_insaspnetcore.identity.sign_in.sign_outsaspnetcore.identity.sign_in.two_factor_clients_rememberedaspnetcore.identity.sign_in.two_factor_clients_forgotten
ASP.NET Core에서 메트릭을 사용하는 방법에 대한 자세한 내용은 ASP.NET Core 메트릭을 참조하세요.
알려진 API 엔드포인트에 대한 로그인 리디렉션 방지 cookie
기본적으로 인증으로 보호되는 알려진 API 엔드포인트에 cookie 대한 인증되지 않은 권한 없는 요청은 이제 로그인 또는 액세스 거부 URI로 리디렉션하지 않고 401 및 403 응답을 생성합니다.
인증되지 않은 요청을 로그인 페이지로 리디렉션하는 것은 일반적으로 인증 실패를 전달하기 위해 HTML 리디렉션이 아닌 401 및 403 상태 코드를 사용하는 API 엔드포인트에 적합하지 않기 때문에 이 변경은 매우 요청되었습니다.
알려진 API 엔드포인트 는 새 인터페이스를 사용하여 식별되며, 새 IApiEndpointMetadata 인터페이스를 구현하는 메타데이터가 다음 항목에 자동으로 추가되었습니다.
-
[ApiController]끝점 - JSON 요청 본문을 읽거나 JSON 응답을 작성하는 최소 API 엔드포인트
- 반환 형식을 사용하는
TypedResults엔드포인트 - SignalR 끝점
이 경우 IApiEndpointMetadatacookie 인증 처리기는 이제 리디렉션하는 대신 적절한 HTTP 상태 코드(인증되지 않은 요청의 경우 401, 금지된 요청의 경우 403)를 반환합니다.
이 새 동작을 방지하고 대상 엔드포인트에 관계없이 인증되지 않거나 권한이 없는 요청에 대해 항상 로그인 및 액세스 거부 URI로 리디렉션하려는 경우 다음과 같이 이벤트 및 RedirectToLogin 이벤트를 재정 RedirectToAccessDenied 의할 수 있습니다.
builder.Services.AddAuthentication()
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
options.Events.OnRedirectToAccessDenied = context =>
{
context.Response.Redirect(context.RedirectUri);
return Task.CompletedTask;
};
});
이 호환성이 손상되는 변경에 대한 자세한 내용은 ASP.NET Core 호환성이 손상되는 변경 공지 사항을 참조하세요.
Miscellaneous
이 섹션에서는 .NET 10의 기타 새 기능에 대해 설명합니다.
예외 처리기 진단 표시 안 함 구성
진단 출력을 제어하기 위해 ExceptionHandlerOptions.SuppressDiagnosticsCallback에 새 구성 옵션이 추가되었습니다. 이 콜백은 요청 및 예외에 대한 컨텍스트를 전달하므로 미들웨어가 예외 로그 및 기타 원격 분석을 작성해야 하는지 여부를 결정하는 논리를 추가할 수 있습니다.
이 설정은 예외가 일시적이거나 예외 처리기 미들웨어에 의해 처리된 것을 알고 있으며, 오류 로그를 관찰 플랫폼에 기록하지 않으려는 경우에 유용합니다.
미들웨어의 기본 동작도 변경되었습니다. 이제 IExceptionHandler에 의해 처리되는 예외에 대해 더 이상 예외 진단을 기록하지 않습니다. 사용자 피드백에 따라 오류 수준에서 처리된 예외를 로깅하는 것은 반환IExceptionHandler.TryHandleAsync될 때 true 바람직하지 않은 경우가 많습니다.
다음을 구성 SuppressDiagnosticsCallback하여 이전 동작으로 되돌릴 수 있습니다.
app.UseExceptionHandler(new ExceptionHandlerOptions
{
SuppressDiagnosticsCallback = context => false;
});
이 호환성이 손상되는 변경에 대한 자세한 내용은 다음을 참조하세요 https://github.com/aspnet/Announcements/issues/524.
.localhost Top-Level 도메인에 대한 지원
.localhost TLD(최상위 도메인)는 RFC2606 정의되고 RFC6761 테스트 목적으로 예약되어 있으며 사용자가 다른 도메인 이름과 마찬가지로 로컬로 사용할 수 있습니다. 즉, 이러한 RFC에 따라 IP 루프백 주소로 확인되는 로컬과 같은 myapp.localhost 이름을 사용할 수 있습니다. 또한 최신 상록 브라우저는 모든 *.localhost 이름을 이미 자동으로 IP 루프백 주소(127.0.0.1/::1)로 해석하여, 로컬 컴퓨터에서 localhost에 이미 호스트된 모든 서비스에 대한 별칭 역할을 합니다. 즉, http://localhost:6789에 응답하는 서비스는 http://anything-here.localhost:6789에도 응답하며, 서비스가 추가적인 특정 호스트 이름 확인이나 검증을 수행하지 않는다고 가정하면 그렇습니다.
ASP.NET Core는 로컬 개발 환경에서 ASP.NET Core 애플리케이션을 만들고 실행할 때 쉽게 사용할 수 있도록 TLD를 더 잘 지원하기 .localhost 위해 .NET 10 Preview 7에서 업데이트되었습니다. 다른 이름을 통해 로컬로 실행되는 다른 앱을 사용하면 일부 도메인 이름 관련 웹 사이트 자산(예: 쿠키)을 더 잘 분리할 수 있으며 브라우저 주소 표시줄에 표시된 이름을 통해 검색하는 앱을 더 쉽게 식별할 수 있습니다.
ASP.NET Core의 기본 제공 HTTP 서버Kestrel는 이제 *.localhost을 통해 설정된 모든 이름을 로컬 루프백 주소로 올바르게 처리하므로 모든 외부 주소(즉, 대신 바인딩)가 아닌 해당 이름에 127.0.0.1/::10.0.0.0/::바인딩됩니다. 여기에는 "applicationUrl" 파일에 구성된 시작 프로필의 속성과 환경 변수가 포함 ASPNETCORE_URLS 됩니다. 주소 .localhost 에서 Kestrel 수신 대기하도록 구성된 경우 두 이름을 모두 사용할 수 있음을 분명히 하기 위해 주소.localhost 주소 모두 localhost 에 대한 정보 메시지를 기록합니다.
웹 브라우저는 *.localhost 이름을 로컬 루프백 주소로 자동으로 확인하지만, 다른 앱은 *.localhost 이름을 일반 도메인 이름으로 간주하고, 해당 DNS 스택을 통해 확인을 시도할 수 있습니다. DNS 구성에서 *.localhost 이름을 주소로 변환하지 못하면 연결에 실패합니다. 웹 브라우저에 없는 경우 일반 localhost 이름을 계속 사용하여 앱을 처리할 수 있습니다.
인증서가 도메인 이름에 사용할 수 있도록 dotnet dev-certs https(명령 포함*.dev.localhost)가 업데이트되었습니다. .NET 10 SDK Preview 7을 설치한 후 명령줄에서 실행 dotnet dev-certs https --trust 하여 새 개발자 인증서를 신뢰하여 시스템이 새 인증서를 신뢰하도록 구성되었는지 확인합니다.
*.dev.localhost 이름을 주체 대체 이름(SAN)으로 나열하는 이유는 최상위 도메인 이름에 와일드카드 인증서를 사용하는 것이 유효하지 않기 때문입니다.
ASP.NET Core Empty(web) 및 Blazor Web App (blazor)에 대한 프로젝트 템플릿은 지정된 경우 만든 프로젝트가 도메인 이름 접미사를 사용하도록 .dev.localhost 구성하고 프로젝트 이름과 결합하여 다음과 같은 https://myapp.dev.localhost:5036주소에서 앱을 검색할 수 있도록 하는 새 옵션으로 업데이트되었습니다.
$ dotnet new web -n MyApp --localhost-tld
The template "ASP.NET Core Empty" was created successfully.
Processing post-creation actions...
Restoring D:\src\MyApp\MyApp.csproj:
Restore succeeded.
$ cd .\MyApp\
$ dotnet run --launch-profile https
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://myapp.dev.localhost:7099
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:7099/
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://myapp.dev.localhost:5036
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5036/
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: D:\src\local\10.0.1xx\MyApp
MVC 및 최소 API의 Json+PipeReader 역직렬화 지원
홍보: https://github.com/dotnet/aspnetcore/pull/62895
MVC, 최소 API 및 HttpRequestJsonExtensions.ReadFromJsonAsync 메서드는 모두 애플리케이션의 코드 변경 없이 새 Json+PipeReader 지원을 사용하도록 업데이트되었습니다.
대부분의 애플리케이션에서 이 지원을 추가해도 해당 동작에는 영향을 주지 않습니다. 그러나 애플리케이션에서 사용자 지정 JsonConverter을 사용하는 경우 변환기가 올바르게 처리 Utf8JsonReader.HasValueSequence 되지 않을 가능성이 있습니다. 이로 인해 역직렬화할 때와 같은 ArgumentOutOfRangeException데이터 및 오류가 누락될 수 있습니다.
빠른 해결 방법(특히 사용 중인 사용자 지정 JsonConverter 을 소유하지 않은 경우)은 스위치를 "Microsoft.AspNetCore.UseStreamBasedJsonParsing"AppContext 로 설정하는 것입니다 "true". 이는 임시 해결 방법이어야 하며 JsonConverter 지원 HasValueSequence하도록 업데이트해야 합니다.
JsonConverter 구현을 수정하려면 ReadOnlySequence 배열을 할당하는 빠른 방법이 있고, 그 예시는 다음과 같습니다.
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
// previous code
}
또한 ReadOnlySequence 처리를 위한 별도의 코드 경로를 포함하는 더 복잡하지만 성능이 뛰어난 수정 방법도 있습니다.
public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.HasValueSequence)
{
reader.ValueSequence;
// ReadOnlySequence optimized path
}
else
{
reader.ValueSpan;
// ReadOnlySpan optimized path
}
}
메모리 풀에서 자동 제거
이제 애플리케이션이 유휴 상태이거나 부하가 적을 때 , IIS 및 HTTP.sys 사용하는 Kestrel메모리 풀이 메모리 블록을 자동으로 제거합니다. 이 기능은 자동으로 실행되며 수동으로 사용하도록 설정하거나 구성할 필요가 없습니다.
메모리 제거가 중요한 이유
이전에는 풀에서 할당한 메모리가 사용되지 않는 경우에도 예약된 상태로 유지됩니다. 이 기능은 앱이 일정 기간 동안 유휴 상태일 때 메모리를 다시 시스템으로 해제합니다. 이 제거는 전체 메모리 사용량을 줄이고 다양한 워크로드에서 애플리케이션의 응답성을 유지하는 데 도움이 됩니다.
메모리 제거 메트릭 사용
메트릭이 서버 구현에서 사용하는 기본 메모리 풀에 추가되었습니다. 새 메트릭은 이름 "Microsoft.AspNetCore.MemoryPool"아래에 있습니다.
메트릭 및 사용 방법에 대한 자세한 내용은 ASP.NET Core 메트릭을 참조하세요.
메모리 풀 관리
.NET 10은 불필요한 메모리 블록을 제거하여 메모리 풀을 보다 효율적으로 사용하는 것 외에도 메모리 풀을 만드는 환경을 개선합니다. 기본 제공 IMemoryPoolFactory 및 MemoryPoolFactory 구현을 제공하여 이 작업을 수행합니다. 종속성 주입을 통해 애플리케이션에서 구현을 사용할 수 있습니다.
다음 코드 예제에서는 기본 제공 메모리 풀 팩터리 구현을 사용하여 메모리 풀을 만드는 간단한 백그라운드 서비스를 보여 줍니다. 이러한 풀은 자동 제거 기능의 이점을 누릴 수 있습니다.
public class MyBackgroundService : BackgroundService
{
private readonly MemoryPool<byte> _memoryPool;
public MyBackgroundService(IMemoryPoolFactory<byte> factory)
{
_memoryPool = factory.Create();
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(20, stoppingToken);
// do work that needs memory
var rented = _memoryPool.Rent(100);
rented.Dispose();
}
catch (OperationCanceledException)
{
return;
}
}
}
}
다음 예제와 같이 IMemoryPoolFactory를 구현하고 종속성 주입으로 등록할 사용자 고유의 메모리 풀 팩터리를 사용하려면, 해당 클래스를 만드세요. 이러한 방식으로 만든 메모리 풀은 사용자 지정 팩터리에서 유사한 제거 논리를 구현하지 않는 한 자동 제거 기능을 활용하지 않습니다.
services.AddSingleton<IMemoryPoolFactory<byte>,
CustomMemoryPoolFactory>();
public class CustomMemoryPoolFactory : IMemoryPoolFactory<byte>
{
public MemoryPool<byte> Create()
{
// Return a custom MemoryPool implementation
// or the default, as is shown here.
return MemoryPool<byte>.Shared;
}
}
HTTP.sys 대한 사용자 지정 가능한 보안 설명자
이제 HTTP.sys 요청 큐에 대한 사용자 지정 보안 설명자를 지정할 수 있습니다. 새 RequestQueueSecurityDescriptor 속성을 사용하면 요청 큐에 HttpSysOptions 대한 액세스 권한을 보다 세부적으로 제어할 수 있습니다. 이 세분화된 컨트롤을 사용하면 애플리케이션의 요구에 맞게 보안을 조정할 수 있습니다.
새 속성으로 수행할 수 있는 작업
HTTP.sys 요청 큐 는 애플리케이션이 처리할 준비가 될 때까지 들어오는 HTTP 요청을 일시적으로 저장하는 커널 수준 구조입니다. 보안 설명자를 사용자 지정하면 요청 큐에 대한 특정 사용자 또는 그룹 액세스를 허용하거나 거부할 수 있습니다. 이는 운영 체제 수준에서 HTTP.sys 요청 처리를 제한하거나 위임하려는 시나리오에서 유용합니다.
새 속성을 사용하는 방법
이 속성은 RequestQueueSecurityDescriptor 새 요청 큐를 만들 때만 적용됩니다. 이 속성은 기존 요청 큐에 영향을 주지 않습니다. 이 속성을 사용하려면 HTTP.sys 서버를 구성할 때 인스턴스로 설정합니다 GenericSecurityDescriptor .
예를 들어 다음 코드는 인증된 모든 사용자를 허용하지만 게스트를 거부합니다.
using System.Security.AccessControl;
using System.Security.Principal;
using Microsoft.AspNetCore.Server.HttpSys;
// Create a new security descriptor
var securityDescriptor = new CommonSecurityDescriptor(isContainer: false, isDS: false, sddlForm: string.Empty);
// Create a discretionary access control list (DACL)
var dacl = new DiscretionaryAcl(isContainer: false, isDS: false, capacity: 2);
dacl.AddAccess(
AccessControlType.Allow,
new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
dacl.AddAccess(
AccessControlType.Deny,
new SecurityIdentifier(WellKnownSidType.BuiltinGuestsSid, null),
-1,
InheritanceFlags.None,
PropagationFlags.None
);
// Assign the DACL to the security descriptor
securityDescriptor.DiscretionaryAcl = dacl;
// Configure HTTP.sys options
var builder = WebApplication.CreateBuilder();
builder.WebHost.UseHttpSys(options =>
{
options.RequestQueueSecurityDescriptor = securityDescriptor;
});
자세한 내용은 ASP.NET Core에서HTTP.sys 웹 서버 구현을 참조하세요.
최상위 문을 사용한 앱 테스트에 대한 지원 향상
.NET 10은 이제 최상위 문을 사용하는 앱을 테스트하는 데 더 나은 지원을 제공합니다. 이전에 개발자는 테스트 프로젝트에서 public partial class Program참조할 수 있도록 Program.cs 파일에 Program class 수동으로 추가해야 했습니다. C# 9의 최상위 문 기능이 public partial class Program으로 선언된 Program class을 생성했기 때문에 이 필요했습니다.
.NET 10에서는 프로그래머가 명시적으로 선언하지 않은 경우 원본 생성기 사용하여 public partial class Program 선언을 생성합니다. 또한 public partial class Program 명시적으로 선언된 시기를 감지하고 개발자에게 제거하도록 권고하기 위해 분석기가 추가되었습니다.
이 기능에 기여한 PR은 다음과 같습니다.
새로운 JSON 패치 구현을 System.Text.Json로 사용하여
- JSON 문서에 적용할 변경 내용을 설명하는 표준 형식입니다.
- RFC 6902에 정의되며 JSON 리소스에 대한 부분 업데이트를 수행하기 위해 RESTful API에서 널리 사용됩니다.
- JSON 문서를 수정하기 위해 적용할 수 있는 작업 시퀀스(예: 추가, 제거, 바꾸기, 이동, 복사, 테스트)를 나타냅니다.
웹앱에서 JSON 패치는 일반적으로 PATCH 작업에서 리소스의 부분 업데이트를 수행하는 데 사용됩니다. 클라이언트는 업데이트를 위해 전체 리소스를 보내는 대신 변경 내용만 포함하는 JSON 패치 문서를 보낼 수 있습니다. 패치를 사용하면 페이로드 크기가 줄어들고 효율성이 향상됩니다.
이 릴리스에서는 Microsoft.AspNetCore.JsonPatch serialization을 기반으로 한 System.Text.Json의 새로운 구현을 소개합니다. 이 기능은 다음과 같습니다.
-
System.Text.Json라이브러리를 활용하여 최신 .NET 관행에 부합하며, .NET에 최적화되었습니다. - 레거시
Newtonsoft.Json기반 구현에 비해 향상된 성능과 메모리 사용량을 제공합니다.
다음 벤치마크는 새 System.Text.Json 구현의 성능을 레거시 Newtonsoft.Json 구현과 비교합니다.
| Scenario | Implementation | Mean | 할당된 메모리 |
|---|---|---|---|
| 애플리케이션 벤치마크 | Newtonsoft.JsonPatch | 271.924 μs | 25KB |
| System.Text.JsonPatch | 1.584 μs | 3KB | |
| 역직렬화 벤치마크 | Newtonsoft.JsonPatch | 19.261 μs | 43KB |
| System.Text.JsonPatch | 7.917 μs | 7KB |
이러한 벤치마크는 새로운 구현을 통해 상당한 성능 향상과 메모리 사용 감소에 대해 강조합니다.
Notes:
- 새 구현은 기존 구현을 직접 대체하는 것이 아닙니다. 특히 새 구현은 예를 들어 ExpandoObject동적 형식을 지원하지 않습니다.
- JSON 패치 표준에는 고유한 보안 위험이 있습니다. 이러한 위험은 JSON 패치 표준에 내재되어 있으므로 새 구현은 내재된 보안 위험을 완화하려고 시도하지 않습니다. JSON 패치 문서가 대상 개체에 안전하게 적용되도록 하는 것은 개발자의 책임입니다. 자세한 내용은 보안 위험 완화 섹션을 참조하세요 .
Usage
JSON 패치 지원을 System.Text.Json 사용하도록 설정하려면 Microsoft.AspNetCore.JsonPatch.SystemTextJson NuGet 패키지를 설치합니다.
dotnet add package Microsoft.AspNetCore.JsonPatch.SystemTextJson --prerelease
이 패키지는 JsonPatchDocument<T> 형식의 개체에 대한 JSON 패치 문서를 나타내는 클래스와 T를 사용하여 JSON 패치 문서를 직렬화 및 역직렬화하기 위한 사용자 지정 논리를 제공합니다.
JsonPatchDocument<T> 클래스의 핵심 메서드는 ApplyTo 형식의 대상 객체에 패치 작업을 적용하는 T입니다.
다음 예제에서는 메서드를 사용하여 개체에 ApplyTo JSON 패치 문서를 적용하는 방법을 보여 줍니다.
예: 다음을 적용합니다. JsonPatchDocument
아래 예제에서는 다음을 보여줍니다.
-
add,replace및remove작업입니다. - 중첩된 속성에 대한 작업입니다.
- 배열에 새 항목 추가
- JSON 패치 문서에서 JSON 문자열 열거형 변환기 사용
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com",
PhoneNumbers = [new() {Number = "123-456-7890", Type = PhoneNumberType.Mobile}],
Address = new Address
{
Street = "123 Main St",
City = "Anytown",
State = "TX"
}
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/FirstName", "value": "Jane" },
{ "op": "remove", "path": "/Email"},
{ "op": "add", "path": "/Address/ZipCode", "value": "90210" },
{
"op": "add",
"path": "/PhoneNumbers/-",
"value": { "Number": "987-654-3210", "Type": "Work" }
}
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document
patchDoc!.ApplyTo(person);
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// {
// "firstName": "Jane",
// "lastName": "Doe",
// "address": {
// "street": "123 Main St",
// "city": "Anytown",
// "state": "TX",
// "zipCode": "90210"
// },
// "phoneNumbers": [
// {
// "number": "123-456-7890",
// "type": "Mobile"
// },
// {
// "number": "987-654-3210",
// "type": "Work"
// }
// ]
// }
메서드 ApplyTo는 일반적으로 System.Text.Json 처리를 위한 규칙 및 옵션을 JsonPatchDocument에 따라 따르며, 다음 옵션에 의해 제어되는 동작을 포함합니다.
-
NumberHandling: 문자열에서 숫자 속성을 읽는지 여부입니다. -
PropertyNameCaseInsensitive: 속성 이름이 대/소문자를 구분하는지 여부입니다.
System.Text.Json와 새로운 JsonPatchDocument<T> 구현 간의 주요 차이점:
- 대상 객체의 런타임 형식이, 선언된 형식이 아닌, 어떤 속성이
ApplyTo패치되는지를 결정합니다. -
System.Text.Json역직렬화는 선언된 형식을 사용하여 적격 속성을 식별합니다.
예: 오류 처리와 JsonPatchDocument 함께 적용
JSON 패치 문서를 적용할 때 발생할 수 있는 다양한 오류가 있습니다. 예를 들어 대상 개체에 지정된 속성이 없거나 지정된 값이 속성 형식과 호환되지 않을 수 있습니다.
JSON 패치는 test 연산도 지원합니다. 이 test 작업은 지정된 값이 대상 속성과 같은지 확인하고 그렇지 않은 경우 오류를 반환합니다.
다음 예제에서는 이러한 오류를 정상적으로 처리하는 방법을 보여 줍니다.
Important
메서드에 전달된 개체가 ApplyTo 현재 위치에서 수정됩니다. 작업이 실패할 경우 이러한 변경 내용을 삭제하는 것은 호출자의 책임입니다.
// Original object
var person = new Person {
FirstName = "John",
LastName = "Doe",
Email = "johndoe@gmail.com"
};
// Raw JSON Patch document
var jsonPatch = """
[
{ "op": "replace", "path": "/Email", "value": "janedoe@gmail.com"},
{ "op": "test", "path": "/FirstName", "value": "Jane" },
{ "op": "replace", "path": "/LastName", "value": "Smith" }
]
""";
// Deserialize the JSON Patch document
var patchDoc = JsonSerializer.Deserialize<JsonPatchDocument<Person>>(jsonPatch);
// Apply the JSON Patch document, catching any errors
Dictionary<string, string[]>? errors = null;
patchDoc!.ApplyTo(person, jsonPatchError =>
{
errors ??= new ();
var key = jsonPatchError.AffectedObject.GetType().Name;
if (!errors.ContainsKey(key))
{
errors.Add(key, new string[] { });
}
errors[key] = errors[key].Append(jsonPatchError.ErrorMessage).ToArray();
});
if (errors != null)
{
// Print the errors
foreach (var error in errors)
{
Console.WriteLine($"Error in {error.Key}: {string.Join(", ", error.Value)}");
}
}
// Output updated object
Console.WriteLine(JsonSerializer.Serialize(person, serializerOptions));
// Output:
// Error in Person: The current value 'John' at path 'FirstName' is not equal
// to the test value 'Jane'.
// {
// "firstName": "John",
// "lastName": "Smith", <<< Modified!
// "email": "janedoe@gmail.com", <<< Modified!
// "phoneNumbers": []
// }
보안 위험 완화
패키지를 사용하는 Microsoft.AspNetCore.JsonPatch.SystemTextJson 경우 잠재적인 보안 위험을 이해하고 완화하는 것이 중요합니다. 다음 섹션에서는 JSON 패치와 관련된 식별된 보안 위험을 간략하게 설명하고 패키지의 안전한 사용을 보장하기 위한 권장 완화 방법을 제공합니다.
Important
이 목록은 전체 위협 목록이 아닙니다. 앱 개발자는 자체 위협 모델 검토를 수행하여 앱별 포괄적인 목록을 결정하고 필요에 따라 적절한 완화를 마련해야 합니다. 예를 들어 패치 작업에 컬렉션을 노출하는 앱은 해당 작업이 컬렉션의 시작 부분에 요소를 삽입하거나 제거하는 경우 알고리즘 복잡성 공격의 가능성을 고려해야 합니다.
자체 앱에 대해 포괄적인 위협 모델을 실행하고 아래 권장 완화를 따르는 동안 식별된 위협을 해결함으로써 이러한 패키지의 소비자는 보안 위험을 최소화하면서 JSON 패치 기능을 앱에 통합할 수 있습니다.
이러한 패키지의 소비자는 다음을 비롯한 보안 위험을 최소화하면서 JSON 패치 기능을 앱에 통합할 수 있습니다.
- 자체 앱에 대해 포괄적인 위협 모델을 실행합니다.
- 식별된 위협을 해결합니다.
- 다음 섹션의 권장 완화를 따릅니다.
메모리 증폭을 통한 DoS(서비스 거부)
-
시나리오: 악의적인 클라이언트가
copy큰 개체 그래프를 여러 번 복제하여 과도한 메모리 소비를 초래하는 작업을 제출합니다. - 영향: OOM(잠재적 아웃-Of-Memory) 조건으로 인해 서비스 중단이 발생합니다.
-
Mitigation:
- 를 호출
ApplyTo하기 전에 들어오는 JSON 패치 문서의 크기와 구조에 대한 유효성을 검사합니다. - 유효성 검사는 앱별로 지정되어야 하지만 예제 유효성 검사는 다음과 유사하게 표시될 수 있습니다.
- 를 호출
public void Validate(JsonPatchDocument<T> patch)
{
// This is just an example. It's up to the developer to make sure that
// this case is handled properly, based on the app's requirements.
if (patch.Operations.Where(op=>op.OperationType == OperationType.Copy).Count()
> MaxCopyOperationsCount)
{
throw new InvalidOperationException();
}
}
비즈니스 논리 전복
- 시나리오: 패치 작업은 암시적 불변성(예: 내부 플래그, ID 또는 계산 필드)을 가진 필드를 조작하여 비즈니스 제약 조건을 위반할 수 있습니다.
- 영향: 데이터 무결성 문제 및 의도하지 않은 앱 동작.
-
Mitigation:
- 수정해도 안전한 명시적으로 정의된 속성과 함께 POCO 개체를 사용합니다.
- 대상 개체에 중요한 속성이나 보안에 중요한 속성이 노출되는 것을 방지합니다.
- POCO 개체가 사용되지 않는 경우 작업을 적용한 후 패치된 개체의 유효성을 검사하여 비즈니스 규칙과 불변 조건이 위반되지 않도록 합니다.
인증 및 권한 부여
- 시나리오: 인증되지 않거나 권한이 없는 클라이언트는 악의적인 JSON 패치 요청을 보냅니다.
- 영향: 중요한 데이터를 수정하거나 앱 동작을 방해하는 무단 액세스입니다.
-
Mitigation:
- 적절한 인증 및 권한 부여 메커니즘을 사용하여 JSON 패치 요청을 수락하는 엔드포인트를 보호합니다.
- 적절한 권한이 있는 신뢰할 수 있는 클라이언트 또는 사용자에 대한 액세스를 제한합니다.
RedirectHttpResult.IsLocalUrl 사용하여 URL이 로컬인지 감지
새 RedirectHttpResult.IsLocalUrl(url) 도우미 메서드를 사용하여 URL이 로컬인지 검색합니다. URL은 다음이 true인 경우 로컬로 간주됩니다.
IsLocalUrl URL로 리디렉션하기 전에 유효성을 검사하여 오픈 리디렉션 공격을 방지하는 데 유용합니다.
if (RedirectHttpResult.IsLocalUrl(url))
{
return Results.LocalRedirect(url);
}
이 기여에 @martincostello 감사합니다!
파괴적 변경
.NET의 호환성이 손상되는 변경에 대한 문서를 사용하여 앱을 최신 버전의 .NET으로 업그레이드할 때 적용할 수 있는 호환성이 손상되는 변경 내용을 찾을 수 있습니다.
ASP.NET Core