이 문서에서는 관련 설명서에 대한 링크와 함께 .NET 10의 ASP.NET Core에서 가장 중요한 변경 내용을 강조 표시합니다.
이 문서는 새 미리 보기 릴리스를 사용할 수 있게 됨에 따라 업데이트됩니다. 주요 변경 사항은 .NET의 중대한 변경 사항을 참조하세요.
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 샘플에는 웹 팜 호스팅 시나리오에 암호화된 분산 토큰 캐시를 사용하는 방법에 대한 새로운 지침도 포함되어 있습니다.
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 스크립트는 자동 압축 및 지문을 사용하는 정적 웹 자산으로 제공됩니다.
자세한 내용은 다음 리소스를 참조하세요.
경로 템플릿 강조 표시
이제 [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.EnableMatchAllForQueryStringAndFragment
AppContext
다음으로 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));
}
응답 스트리밍이 옵트인되고 옵트아웃하는 방법
이전 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의 경우 독립 실행형 Blazor WebAssembly 앱에 대한 JavaScript 모듈의 클라이언트 쪽 지문을 옵트인할 수 있습니다.
빌드/게시하는 동안, 독립 실행형 Blazor WebAssembly 앱에서 프레임워크는 빌드 중에 계산된 값으로 index.html
의 자리 표시자를 대체하여 정적 자산을 고유하게 식별합니다. 지문 정보는 스크립트 파일 이름에 blazor.webassembly.js
삽입됩니다.
지문 기능을 채택하려면 wwwwoot/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>
지문 표식이 있는 index.html
모든 스크립트는 프레임워크에서 지문 처리됩니다. 예를 들어 앱의 scripts.js
폴더에 이름이 지정된 wwwroot/js
스크립트 파일은 파일 확장명 앞에 추가하여 #[.{fingerprint}]
지문으로 표시됩니다(.js
).
<script src="js/scripts#[.{fingerprint}].js"></script>
독립 실행형 JS 앱에서 추가 Blazor WebAssembly 모듈의 지문을 지정하려면 앱의 프로젝트 파일(<StaticWebAssetFingerprintPattern>
)에서 .csproj
속성을 사용하세요.
다음 예제에서는 앱에서 개발자가 제공한 모든 파일에 지문이 추가됩니다 .mjs
.
<StaticWebAssetFingerprintPattern Include="JSModule" Pattern="*.mjs"
Expression="#[.{fingerprint}]!" />
파일은 자동으로 가져오기 맵에 배치됩니다.
- Blazor Web App CSR에 대해 자동으로.
- 이전 지침에 따라 독립 실행형 Blazor WebAssembly 앱에서 모듈 지문 기능을 활성화할 경우
JavaScript interop에 대한 가져오기에서 지문 파일을 해결하기 위해 브라우저가 가져오기 맵을 사용합니다.
독립 실행형 Blazor WebAssembly 앱에서 환경 설정
이 Properties/launchSettings.json
파일은 독립 실행형 앱의 환경을 제어하는 데 더 이상 사용되지 않습니다 Blazor WebAssembly .
.NET 10부터 앱의 프로젝트 파일(<WasmApplicationEnvironmentName>
)에서 .csproj
속성을 사용하여 환경을 설정합니다.
다음 예제에서는 앱의 환경을 다음으로 Staging
설정합니다.
<WasmApplicationEnvironmentName>Staging</WasmApplicationEnvironmentName>
기본 환경은 다음과 같습니다.
-
Development
빌드용입니다. -
Production
게시용입니다.
인라인된 부팅 구성 파일
Blazor의 부팅 구성은 .NET 10이 릴리스되기 전에 blazor.boot.json
라는 파일에 있었으나, 이제 dotnet.js
스크립트로 인라인되었습니다. 이는 개발자가 다음과 같은 경우와 같이 파일과 blazor.boot.json
직접 상호 작용하는 개발자에게만 영향을 줍니다.
- ASP.NET Core Blazor WebAssembly .NET 번들 캐싱 및 무결성 검사 실패의 지침에 따라 문제 해결 무결성 PowerShell 스크립트를 사용하여 게시된 자산에 대한 파일 무결성을 확인합니다.
- Host의 지침에 따라 기본 웹실 파일 형식을 사용하지 않는 경우 DLL 파일의 파일 이름 확장명을 변경하고 ASP.NET CoreBlazor WebAssembly를 배포합니다.
현재 이전 방법에 대한 문서화된 대체 전략은 없습니다. 앞의 전략 중 하나가 필요한 경우 두 문서의 맨 아래에 있는 설명서 열기 문제 링크를 사용하여 시나리오를 설명하는 새 설명서 문제를 엽니다.
구성 요소 및 서비스에서 상태를 유지하기 위한 선언적 모델
이제 특성을 사용하여 [SupplyParameterFromPersistentComponentState]
구성 요소 및 서비스에서 유지할 상태를 선언적으로 지정할 수 있습니다. 이 특성이 있는 속성은 미리 렌더링하는 동안 서비스를 사용하여 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 {
[SupplyParameterFromPersistentComponentState]
public List<Movie>? MoviesList { get; set; }
protected override async Task OnInitializedAsync()
{
MoviesList ??= await MovieService.GetMoviesAsync();
}
}
상태는 동일한 형식의 여러 구성 요소에 대해 직렬화할 수 있으며, 사용자 지정 서비스 유형 및 렌더링 모드로 구성 요소 작성기(RegisterPersistentService
)를 호출 RazorAddRazorComponents 하여 앱에서 사용할 서비스에서 선언적 상태를 설정할 수 있습니다. 자세한 내용은 Prerender ASP.NET Core Razor 구성 요소를 참조하세요.
새 JavaScript interop 기능
Blazor 는 다음 JS interop 기능에 대한 지원을 추가합니다.
- 생성자 함수를 사용하여 JS 개체 인스턴스를 만들고 인스턴스를 참조하기 위해 IJSObjectReference/IJSInProcessObjectReference .NET 핸들을 가져옵니다.
- JS 개체 속성의 데이터 속성 및 접근자 속성 값을 읽거나 수정합니다.
다음 비동기 메서드는 기존 IJSRuntime 메서드와 동일한 범위 지정 동작을 가진 IJSObjectReference 및 IJSRuntime.InvokeAsync에서 사용할 수 있습니다.
InvokeNewAsync(string identifier, object?[]? args)
: 지정된 생성자 함수를 JS 비동기적으로 호출합니다. 함수는 연산자를 사용하여 호출됩니다new
. 다음 예제jsInterop.TestClass
에서는 생성자 함수가 있는 클래스이며classRef
다음과 같습니다 IJSObjectReference.var classRef = await JSRuntime.InvokeNewAsync("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에서 사용할 수 있습니다.
InvokeNew(string identifier, object?[]? args)
: 지정된 생성자 함수를 JS 동기적으로 호출합니다. 함수는 연산자를 사용하여 호출됩니다new
. 다음 예제jsInterop.TestClass
에서는 생성자 함수가 있는 클래스이며classRef
다음과 같습니다 IJSInProcessObjectReference.var inProcRuntime = ((IJSInProcessRuntime)JSRuntime); var classRef = inProcRuntime.InvokeNew("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 앱에 새로운 성능 프로파일링 및 진단 카운터가 제공됩니다. 자세한 내용은 다음 문서를 참조하세요.
미리 로드된 Blazor 프레임워크 정적 자산
프레임워크 Blazor Web App정적 자산은 헤더를 사용하여 Link
자동으로 미리 로드되므로 초기 페이지를 가져오고 렌더링하기 전에 브라우저에서 리소스를 미리 로드할 수 있습니다. 독립 실행형 Blazor WebAssembly 앱에서 프레임워크 자산은 브라우저 index.html
페이지 처리 초기에 우선 순위가 높은 다운로드 및 캐싱을 위해 예약됩니다.
자세한 내용은 ASP.NET Core Blazor 정적 파일참조하세요.
NavigationManager.NavigateTo
은 더 이상 NavigationException
을 발생시키지 않습니다.
이전에 정적 서버 쪽 렌더링(SSR) 중에 `NavigationManager.NavigateTo`를 호출하면 `NavigationException` 예외가 발생하여 리디렉션 응답으로 변환되기 전에 실행이 중단되었습니다. 이로 인해 디버깅 중에 혼란이 발생했으며 대화형 렌더링과 일치하지 않았으며, 이후 코드 NavigateTo 가 정상적으로 계속 실행됩니다.
정적 SSR 중에 NavigationManager.NavigateTo를 호출해도 더 이상 NavigationException를 발생시키지 않습니다. 대신 예외를 발생시키지 않고 탐색을 수행하여 대화형 렌더링에 맞춰 일관되게 작동합니다.
throw되는 데 의존하는 NavigationException 코드를 업데이트해야 합니다. 예를 들어 기본 BlazorIdentity UI에서 IdentityRedirectManager
를 호출한 후에 대화형 렌더링 중에는 호출되지 않도록 하기 위해 InvalidOperationException가 이전에 RedirectTo
오류를 발생시켰습니다. 이 예외 및 특성은 [DoesNotReturn]
이제 제거되어야 합니다.
throw하는 이전 동작으로 NavigationException되돌리려면 다음 AppContext 스위치를 설정합니다.
AppContext.SetSwitch(
"Microsoft.AspNetCore.Components.Endpoints.NavigationManager.EnableThrowNavigationException",
isEnabled: true);
정적 SSR 및 전역 대화형 렌더링에 사용되는 NavigationManager
응답을 찾을 수 없음
NavigationManager 이제 정적 서버 쪽 렌더링(정적 SSR) 또는 전역 대화형 렌더링 중에 요청된 리소스를 찾을 수 없는 시나리오를 처리하는 메서드가 포함되어 NotFound
있습니다.
-
정적 서버 쪽 렌더링(정적 SSR): 호출
NotFound
은 HTTP 상태 코드를 404로 설정합니다. - 스트리밍 렌더링: 응답이 이미 시작된 경우 예외를 throw합니다.
-
대화형 렌더링: 라우터(Blazor)에게 찾을 수 없는 콘텐츠를 렌더링하도록 신호를
Router
보냅니다.
페이지별/구성 요소 렌더링 지원은 2025년 6월 미리 보기 5에 대해 계획되어 있습니다.
NavigationManager.OnNotFound
가 호출될 때 알림을 위해 NotFound
이벤트를 사용할 수 있습니다.
자세한 내용 및 예제는 ASP.NET Core Blazor 라우팅 및 탐색을 참조하세요.
Blazor라우터에 매개 변수가 있습니다.NotFoundPage
Blazor 이제 존재하지 않는 페이지로 이동할 때 "찾을 수 없음" 페이지를 표시하는 향상된 방법을 제공합니다. 매개변수 NavigationManager.NotFound
를 사용하여 페이지 유형을 Router
구성 요소에 전달하면 NotFoundPage
시 렌더링할 페이지를 지정할 수 있습니다. 이 방법은 라우팅을 지원하고 코드 다시 실행 미들웨어에서 작동하며 비NotFound
시나리오와도 호환되므로 이전 Blazor 조각에 권장됩니다.
NotFound
조각과 NotFoundPage
가 모두 정의된 경우, NotFoundPage
로 지정된 페이지가 우선합니다.
<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
페이지가 포함됩니다. 이 페이지는 앱에서 호출할 때마다 NavigationManager.NotFound
자동으로 렌더링되므로 일관된 사용자 환경으로 누락된 경로를 보다 쉽게 처리할 수 있습니다.
자세한 내용은 ASP.NET Core Blazor 라우팅 및 탐색을 참조하세요.
메트릭 및 추적
이 릴리스에서는 앱에 대한 포괄적인 메트릭 및 추적 기능을 도입하여 구성 요소 수명 주기, 탐색, 이벤트 처리 및 회로 관리에 대한 Blazor 자세한 관찰 가능성을 제공합니다.
자세한 내용은 ASP.NET Core Blazor 성능 모범 사례를 참조하세요.
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-samples
GitHub 리포지토리)
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 런타임에서 다음 사항에 정의된 유효성 검사를 수행할 수 있습니다.
- 쿼리
- 헤더
- 요청 메시지 본문
유효성 검사는 네임스페이스의 특성을 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();
비고
.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);
});
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
입니다.
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, ProducesResponseTypeAttribute및 ProducesDefaultResponseType 특성은 이제 응답에 대한 설명을 설정하는 선택적 문자열 매개 변수 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는 현재 ProducesResponseType
을 지원하지 않습니다.
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 Web API(네이티브 AOT) 프로젝트 템플릿(짧은 이름webapiaot
)에는 기본적으로 패키지를 사용하는 Microsoft.AspNetCore.OpenApi
OpenAPI 문서 생성에 대한 지원이 포함됩니다. 이 지원은 새 프로젝트를 만들 때 플래그를 --no-openapi
사용하여 사용하지 않도록 설정됩니다.
@sander1095의 커뮤니티 기여입니다. 이 기여 주셔서 감사합니다!
DI 컨테이너에서 IOpenApiDocumentProvider를 지원합니다.
.NET 10의 ASP.NET Core는 DI(종속성 주입) 컨테이너에서 IOpenApiDocumentProvider 를 지원합니다. 개발자는 앱에 삽입 IOpenApiDocumentProvider
하고 이를 사용하여 OpenAPI 문서에 액세스할 수 있습니다. 이 방법은 백그라운드 서비스 또는 사용자 지정 미들웨어와 같은 HTTP 요청 컨텍스트 외부에서 OpenAPI 문서에 액세스하는 데 유용합니다.
이전에는 no-op 구현을 사용하여 HostFactoryResolver
HTTP 서버를 시작하지 않고 애플리케이션 시작 논리를 IServer
실행할 수 있습니다. 새로운 기능은 분산 애플리케이션 호스팅 및 게시를 위한 Aspire 프레임워크의 일부인 Aspire's IDistributedApplicationPublisher에서 영감을 받은 간소화된 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 주석을 포함할 것으로 예상됩니다.
변환기에서 OpenApiSchemas 생성 지원
개발자는 이제 ASP.NET Core OpenAPI 문서 생성과 동일한 논리를 사용하여 C# 형식에 대한 스키마를 생성하고 OpenAPI 문서에 추가할 수 있습니다. 그런 다음, OpenAPI 문서의 다른 위치에서 스키마를 참조할 수 있습니다.
문서, 작업 및 스키마 변환기에 전달되는 컨텍스트에는 형식에 대한 스키마를 생성하는 데 사용할 수 있는 새 GetOrCreateSchemaAsync
메서드가 포함됩니다.
또한 이 메서드에는 생성된 스키마에 대한 추가 메타데이터를 지정하는 선택적 ApiParameterDescription
매개 변수가 있습니다.
OpenAPI 문서에 Document
스키마를 추가할 수 있도록 작업 및 스키마 변환기 컨텍스트에 속성이 추가되었습니다. 이렇게 하면 변환기가 문서의 AddComponent
메서드를 사용하여 OpenAPI 문서에 스키마를 추가할 수 있습니다.
예시
문서, 작업 또는 스키마 변환기에서 이 기능을 사용하려면 컨텍스트에 제공된 메서드를 사용하여 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.NET Preview.18로 업데이트됨
ASP.NET Core OpenAPI 문서 생성에 사용되는 OpenAPI.NET 라이브러리가 v2.0.0-preview18로 업그레이드되었습니다. v2.0.0-preview18 버전은 업데이트된 라이브러리 버전과의 호환성을 향상시킵니다.
이전 v2.0.0-preview17 버전에는 다양한 버그 수정 및 개선 사항이 포함되어 있으며 몇 가지 주요 변경 내용도 도입되었습니다. 중단되는 변경 사항은 문서, 연산, 또는 스키마 변환기를 사용하는 사용자에게만 영향을 미칩니다. 개발자에게 영향을 줄 수 있는 이 버전의 주요 변경 내용은 다음과 같습니다.
인증 및 권한 부여
이 섹션에서는 인증 및 권한 부여를 위한 새로운 기능에 대해 설명합니다.
인증 및 권한 부여 메트릭
ASP.NET Core의 특정 인증 및 권한 부여 이벤트에 대한 메트릭이 추가되었습니다. 이 변경으로 이제 다음 이벤트에 대한 메트릭을 가져올 수 있습니다.
- 인증:
- 인증된 요청 기간
- 도전 횟수
- 카운트 금지
- 로그인 수
- 로그아웃 횟수
- 권한 부여:
- 권한 부여가 필요한 요청 수
다음 이미지는 Aspire 대시보드에서 인증된 요청 기간 메트릭의 예를 보여줍니다.
자세한 내용은 ASP.NET 핵심 권한 부여 및 인증 메트릭을 참조하세요.
기타
이 섹션에서는 .NET 10의 기타 새 기능에 대해 설명합니다.
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
구현과 비교합니다.
시나리오 | 이행 | 평균 | 할당된 메모리 |
---|---|---|---|
애플리케이션 벤치마크 | 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 |
이러한 벤치마크는 새로운 구현을 통해 상당한 성능 향상과 메모리 사용 감소에 대해 강조합니다.
노트:
- 새 구현은 기존 구현을 직접 대체하는 것이 아닙니다. 특히 새 구현은 예를 들어 ExpandoObject동적 형식을 지원하지 않습니다.
- JSON 패치 표준에는 고유한 보안 위험이 있습니다. 이러한 위험은 JSON 패치 표준에 내재되어 있으므로 새 구현은 내재된 보안 위험을 완화하려고 시도하지 않습니다. JSON 패치 문서가 대상 개체에 안전하게 적용되도록 하는 것은 개발자의 책임입니다. 자세한 내용은 보안 위험 완화 섹션을 참조하세요 .
사용법
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
작업은 지정된 값이 대상 속성과 같은지 확인하고 그렇지 않은 경우 오류를 반환합니다.
다음 예제에서는 이러한 오류를 정상적으로 처리하는 방법을 보여 줍니다.
중요합니다
메서드에 전달된 개체가 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 패치와 관련된 식별된 보안 위험을 간략하게 설명하고 패키지의 안전한 사용을 보장하기 위한 권장 완화 방법을 제공합니다.
중요합니다
이 목록은 전체 위협 목록이 아닙니다. 앱 개발자는 자체 위협 모델 검토를 수행하여 앱별 포괄적인 목록을 결정하고 필요에 따라 적절한 완화를 마련해야 합니다. 예를 들어 패치 작업에 컬렉션을 노출하는 앱은 해당 작업이 컬렉션의 시작 부분에 요소를 삽입하거나 제거하는 경우 알고리즘 복잡성 공격의 가능성을 고려해야 합니다.
자체 앱에 대해 포괄적인 위협 모델을 실행하고 아래 권장 완화를 따르는 동안 식별된 위협을 해결함으로써 이러한 패키지의 소비자는 보안 위험을 최소화하면서 JSON 패치 기능을 앱에 통합할 수 있습니다.
이러한 패키지의 소비자는 다음을 비롯한 보안 위험을 최소화하면서 JSON 패치 기능을 앱에 통합할 수 있습니다.
- 자체 앱에 대해 포괄적인 위협 모델을 실행합니다.
- 식별된 위협을 해결합니다.
- 다음 섹션의 권장 완화를 따릅니다.
메모리 증폭을 통한 DoS(서비스 거부)
-
시나리오: 악의적인 클라이언트가
copy
큰 개체 그래프를 여러 번 복제하여 과도한 메모리 소비를 초래하는 작업을 제출합니다. - 영향: OOM(잠재적 아웃-Of-Memory) 조건으로 인해 서비스 중단이 발생합니다.
-
완화 방법:
- 를 호출
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 또는 계산 필드)을 가진 필드를 조작하여 비즈니스 제약 조건을 위반할 수 있습니다.
- 영향: 데이터 무결성 문제 및 의도하지 않은 앱 동작.
-
완화 방법:
- 수정해도 안전한 명시적으로 정의된 속성과 함께 POCO 개체를 사용합니다.
- 대상 개체에 중요한 속성이나 보안에 중요한 속성이 노출되는 것을 방지합니다.
- POCO 개체가 사용되지 않는 경우 작업을 적용한 후 패치된 개체의 유효성을 검사하여 비즈니스 규칙과 불변 조건이 위반되지 않도록 합니다.
인증 및 권한 부여
- 시나리오: 인증되지 않거나 권한이 없는 클라이언트는 악의적인 JSON 패치 요청을 보냅니다.
- 영향: 중요한 데이터를 수정하거나 앱 동작을 방해하는 무단 액세스입니다.
-
완화 방법:
- 적절한 인증 및 권한 부여 메커니즘을 사용하여 JSON 패치 요청을 수락하는 엔드포인트를 보호합니다.
- 적절한 권한이 있는 신뢰할 수 있는 클라이언트 또는 사용자에 대한 액세스를 제한합니다.
RedirectHttpResult.IsLocalUrl
사용하여 URL이 로컬인지 감지
새 RedirectHttpResult.IsLocalUrl(url)
도우미 메서드를 사용하여 URL이 로컬인지 검색합니다. URL은 다음이 true인 경우 로컬로 간주됩니다.
IsLocalUrl
URL로 리디렉션하기 전에 유효성을 검사하여 오픈 리디렉션 공격을 방지하는 데 유용합니다.
if (RedirectHttpResult.IsLocalUrl(url))
{
return Results.LocalRedirect(url);
}
이 기여에 @martincostello 감사합니다!
관련 콘텐츠
ASP.NET Core