ASP.NET Core 7.0의 새로운 기능

이 문서에서는 ASP.NET Core 7.0의 가장 중요한 변경 사항을 중점적으로 설명하고 관련 문서의 링크를 제공합니다.

ASP.NET Core의 속도 제한 미들웨어

Microsoft.AspNetCore.RateLimiting 미들웨어는 속도 제한 미들웨어를 제공합니다. 앱은 속도 제한 정책을 구성한 다음, 정책을 엔드포인트에 연결합니다. 자세한 내용은 ASP.NET Core의 속도 제한 미들웨어를 참조하세요.

인증에서는 단일 체계를 DefaultScheme으로 사용합니다.

인증을 간소화하기 위한 일환으로서 등록된 인증 체계가 단 한 개인 경우 자동으로 DefaultScheme을 사용하며 지정할 필요가 없습니다. 자세한 내용은 DefaultScheme를 참조하세요.

MVC 및 Razor 페이지

MVC 보기 및 Razor 페이지에서 null 허용 모델에 대한 지원

null 허용 페이지 또는 보기 모델은 ASP.NET Core 앱에서 null 상태 검사를 사용할 때 환경을 개선하기 위해 지원됩니다.

@model Product?

MVC 및 API 컨트롤러에서 IParsable<T>.TryParse와 바인딩

IParsable<TSelf>.TryParse API는 바인딩 컨트롤러 작업 매개 변수 값을 지원합니다. 자세한 내용은 IParsable<T>.TryParse와 바인딩을 참조하세요.

7 이전의 ASP.NET Core 버전에서 cookie 동의 유효성 검사는 cookie 값 yes를 사용하여 동의를 나타냅니다. 이제 동의를 나타내는 값을 지정할 수 있습니다. 예를 들어 yes 대신 true를 사용할 수 있습니다.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.Configure<CookiePolicyOptions>(options =>
{
    options.CheckConsentNeeded = context => true;
    options.MinimumSameSitePolicy = SameSiteMode.None;
    options.ConsentCookieValue = "true";
});

var app = builder.Build();

자세한 내용은 cookie 동의 값 사용자 지정을 참조하세요.

API 컨트롤러

API 컨트롤러에서 DI를 통해 매개 변수 바인딩

API 컨트롤러 작업에 대한 매개 변수 바인딩은 형식이 서비스로 구성된 경우 종속성 주입을 통해 매개 변수를 바인딩합니다. 즉, 더 이상 매개 변수에 [FromServices] 특성을 명시적으로 적용할 필요가 없습니다. 다음 코드에서는 두 작업 모두 시간을 반환합니다.

[Route("[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    public ActionResult GetWithAttribute([FromServices] IDateTime dateTime) 
                                                        => Ok(dateTime.Now);

    [Route("noAttribute")]
    public ActionResult Get(IDateTime dateTime) => Ok(dateTime.Now);
}

드문 경우지만 자동 DI는 API 컨트롤러 작업 메서드에서도 사용되는 DI 형식을 갖는 앱을 중단할 수 있습니다. DI 형식을 API 컨트롤러 작업의 인수로 사용하는 것은 일반적이지 않습니다. 매개 변수의 자동 바인딩을 사용하지 않도록 설정하려면 DisableImplicitFromServicesParameters를 설정합니다.

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers();
builder.Services.AddSingleton<IDateTime, SystemDateTime>();

builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.DisableImplicitFromServicesParameters = true;
});

var app = builder.Build();

app.MapControllers();

app.Run();

ASP.NET Core 7.0에서는 앱 시작 시 API 컨트롤러 작업의 인수가 DI에서 오는지 또는 다른 소스에서 오는지 IServiceProviderIsService로 DI 형식을 확인합니다.

API 컨트롤러 작업 매개 변수의 바인딩 소스를 유추하는 새 메커니즘은 다음 규칙을 사용합니다.

  1. 이전에 지정한 BindingInfo.BindingSource는 덮어쓰지 않습니다.
  2. DI 컨테이너에 등록된 복합 형식 매개 변수 BindingSource.Services가 할당됩니다.
  3. DI 컨테이너에 등록된 복합 형식 매개 변수 BindingSource.Body가 할당되지 않습니다.
  4. 모든 경로 템플릿에서 경로 값으로 표시되는 이름의 매개 변수 BindingSource.Path가 할당됩니다.
  5. 다른 모든 매개 변수는 BindingSource.Query입니다.

유효성 검사 오류의 JSON 속성 이름

기본적으로 유효성 검사 오류가 발생하면 모델 유효성 검사는 속성 이름을 오류 키로 사용하여 ModelStateDictionary를 생성합니다. 단일 페이지 앱과 같은 일부 앱은 Web API에서 생성된 유효성 검사 오류에 JSON 속성 이름을 사용할 수 있습니다. 다음 코드는 SystemTextJsonValidationMetadataProvider를 사용하여 JSON 속성 이름을 사용하도록 유효성 검사를 구성합니다.

using Microsoft.AspNetCore.Mvc.ModelBinding.Metadata;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new SystemTextJsonValidationMetadataProvider());
});

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

다음 코드는 Json.NET를 사용할 때 NewtonsoftJsonValidationMetadataProvider를 사용하여 JSON 속성 이름을 사용하도록 유효성 검사를 구성합니다.

using Microsoft.AspNetCore.Mvc.NewtonsoftJson;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(options =>
{
    options.ModelMetadataDetailsProviders.Add(new NewtonsoftJsonValidationMetadataProvider());
}).AddNewtonsoftJson();

var app = builder.Build();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

자세한 내용은 유효성 검사 오류에서 JSON 속성 이름 사용을 참조하세요

최소 API

최소 API 앱의 필터

최소 API 필터를 통해 개발자는 다음을 지원하는 비즈니스 논리를 구현할 수 있습니다.

  • 경로 처리기 전후에서 코드 실행
  • 경로 처리기 호출 중에 제공된 매개 변수 검사 및 수정
  • 경로 처리기의 응답 동작 가로채기

필터는 다음 시나리오에서 유용할 수 있습니다.

  • 엔드포인트로 전송되는 요청 매개 변수 및 본문의 유효성 검사
  • 요청 및 응답에 대한 정보 로깅
  • 요청이 지원되는 API 버전을 대상으로 하는지 확인

자세한 내용은 최소 API 앱의 필터를 참조하세요.

헤더 및 쿼리 문자열에서 배열 및 문자열 값 바인딩

ASP.NET 7에서는 기본 형식의 배열, 문자열 배열 및 StringValues에 쿼리 문자열 바인딩이 지원됩니다.

// Bind query string values to a primitive type array.
// GET  /tags?q=1&q=2&q=3
app.MapGet("/tags", (int[] q) =>
                      $"tag1: {q[0]} , tag2: {q[1]}, tag3: {q[2]}");

// Bind to a string array.
// GET /tags2?names=john&names=jack&names=jane
app.MapGet("/tags2", (string[] names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

// Bind to StringValues.
// GET /tags3?names=john&names=jack&names=jane
app.MapGet("/tags3", (StringValues names) =>
            $"tag1: {names[0]} , tag2: {names[1]}, tag3: {names[2]}");

복합 형식의 배열에 쿼리 문자열 또는 헤더 값 바인딩은 형식에 TryParse가 구현된 경우에 지원됩니다. 자세한 내용은 헤더 및 쿼리 문자열의 배열 및 문자열 값 바인딩을 참조하세요.

자세한 내용은 엔드포인트 요약 또는 설명 추가를 참조하세요.

요청 본문을 Stream 또는 PipeReader로 바인딩

요청 본문을 Stream 또는 PipeReader로 바인딩하여 사용자가 데이터를 처리하여 다음 작업을 수행해야 하는 시나리오를 효율적으로 지원할 수 있습니다.

  • 데이터를 Blob 스토리지에 저장하거나 큐 공급자의 큐에 추가합니다.
  • 작업자 프로세스 또는 클라우드 함수를 사용하여 저장된 데이터를 처리합니다.

예를 들어 데이터는 Azure Queue Storage의 큐에 추가하거나 Azure Blob Storage에 저장할 수 있습니다.

자세한 내용은 요청 본문을 Stream 또는 PipeReader로 바인딩을 참조하세요.

새 Results.Stream 오버로드

버퍼링 없이 기본 HTTP 응답 스트림에 액세스해야 하는 시나리오를 수용하기 위해 새로운 Results.Stream 오버로드를 도입했습니다. 또한 이러한 오버로드는 API가 데이터를 Azure Blob Storage와 같은 HTTP 응답 스트림으로 스트리밍하는 경우를 개선합니다. 다음 예제에서는 ImageSharp를 사용하여 지정된 이미지의 축소된 크기를 반환합니다.

using SixLabors.ImageSharp;
using SixLabors.ImageSharp.Formats.Jpeg;
using SixLabors.ImageSharp.Processing;

var builder = WebApplication.CreateBuilder(args);

var app = builder.Build();

app.MapGet("/process-image/{strImage}", (string strImage, HttpContext http, CancellationToken token) =>
{
    http.Response.Headers.CacheControl = $"public,max-age={TimeSpan.FromHours(24).TotalSeconds}";
    return Results.Stream(stream => ResizeImageAsync(strImage, stream, token), "image/jpeg");
});

async Task ResizeImageAsync(string strImage, Stream stream, CancellationToken token)
{
    var strPath = $"wwwroot/img/{strImage}";
    using var image = await Image.LoadAsync(strPath, token);
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x =>x.Resize(width, height));
    await image.SaveAsync(stream, JpegFormat.Instance, cancellationToken: token);
}

자세한 내용은 스트림 예을 참조하세요.

최소 API에 대한 형식화된 결과

.NET 6에서 IResult 인터페이스는 반환된 개체를 HTTP 응답으로 직렬화하는 JSON에 대한 암시적 지원을 활용하지 않는 최소 API에서 반환된 값을 나타내기 위해 도입되었습니다. 정적 Results 클래스는 다양한 형식의 응답을 나타내는 다양한 IResult 개체를 만드는 데 사용됩니다. 예를 들어 응답 상태 코드를 설정하거나 다른 URL로 리디렉션합니다. 그러나, 이러한 메서드에서 반환된 프레임워크 형식을 구현하는 IResult는 내부적이어서, 특정 IResult 유형이 단위 테스트의 메서드에서 반환됨을 확인하기 어려웠습니다.

.NET 7에서 IResult를 구현하는 형식은 공용이므로 테스트할 때 형식 어설션을 허용합니다. 예시:

[TestClass()]
public class WeatherApiTests
{
    [TestMethod()]
    public void MapWeatherApiTest()
    {
        var result = WeatherApi.GetAllWeathers();
        Assert.IsInstanceOfType(result, typeof(Ok<WeatherForecast[]>));
    }      
}

최소 경로 처리기의 개선된 단위 테스트 가능성

IResult 구현 형식은 이제 Microsoft.AspNetCore.Http.HttpResults네임스페이스에 공개되어 사용할 수 있습니다. 구현 형식은 IResult 람다 대신 명명된 메서드를 사용할 때 최소 경로 처리기에서 단위 테스트를 하는 데 사용할 수 있습니다.

다음 코드에서는 Ok<TValue> 값을 사용합니다.

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    // Arrange
    await using var context = new MockDb().CreateDbContext();

    context.Todos.Add(new Todo
    {
        Id = 1,
        Title = "Test title",
        Description = "Test description",
        IsDone = false
    });

    await context.SaveChangesAsync();

    // Act
    var result = await TodoEndpointsV1.GetTodo(1, context);

    //Assert
    Assert.IsType<Results<Ok<Todo>, NotFound>>(result);

    var okResult = (Ok<Todo>)result.Result;

    Assert.NotNull(okResult.Value);
    Assert.Equal(1, okResult.Value.Id);
}

자세한 내용은 IResult구현 형식을 참조하세요.

새 HttpResult 인터페이스

Microsoft.AspNetCore.Http 네임스페이스의 다음과 같은 인터페이스는 필터 구현의 일반적 유형으로서 런타임 시 IResult 형식을 검색하는 방법을 제공합니다.

자세한 내용은 IHttpResult 인터페이스를 참조하세요.

최소 API에 대한 OpenAPI 개선 사항

Microsoft.AspNetCore.OpenApi NuGet 패키지

Microsoft.AspNetCore.OpenApi 패키지는 엔드포인트에 대한 OpenAPI 사양과의 상호 작용을 허용합니다. 이 패키지는 Microsoft.AspNetCore.OpenApi 패키지에 정의된 OpenAPI 모델과 최소 API에 정의된 엔드포인트 간의 링크 역할을 합니다. 이 패키지는 엔드포인트의 매개 변수, 응답 및 메타데이터를 검사하여 엔드포인트를 설명하는 데 사용되는 OpenAPI 주석 형식을 생성하는 API를 제공합니다.

app.MapPost("/todoitems/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi();

매개 변수를 사용하여 WithOpenApi 호출

WithOpenApi 메서드는 OpenAPI 주석을 수정하는 데 사용할 수 있는 함수를 허용합니다. 예를 들어 다음 코드에서는 엔드포인트의 첫 번째 매개 변수에 설명이 추가됩니다.

app.MapPost("/todo2/{id}", async (int id, Todo todo, TodoDb db) =>
{
    todo.Id = id;
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todoitems/{todo.Id}", todo);
})
.WithOpenApi(generatedOperation =>
{
    var parameter = generatedOperation.Parameters[0];
    parameter.Description = "The ID associated with the created Todo";
    return generatedOperation;
});

엔드포인트 설명 및 요약 제공

이제 최소 API는 OpenAPI 사양 생성에 대한 설명 및 요약을 사용하여 주석 작업을 지원합니다. 확장 메서드 WithDescriptionWithSummary를 호출하거나 특성 [EndpointDescription][EndpointSummary])를 사용할 수 있습니다.

자세한 내용은 최소 API 앱의 OpenAPI를 참조하세요.

IFormFile 및 IFormFileCollection을 사용하여 파일 업로드

이제 최소 API는 IFormFileIFormFileCollection를 사용하여 파일 업로드를 지원합니다. 다음 코드는 IFormFileIFormFileCollection을 사용하여 파일을 업로드합니다.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => "Hello World!");

app.MapPost("/upload", async (IFormFile file) =>
{
    var tempFile = Path.GetTempFileName();
    app.Logger.LogInformation(tempFile);
    using var stream = File.OpenWrite(tempFile);
    await file.CopyToAsync(stream);
});

app.MapPost("/upload_many", async (IFormFileCollection myFiles) =>
{
    foreach (var file in myFiles)
    {
        var tempFile = Path.GetTempFileName();
        app.Logger.LogInformation(tempFile);
        using var stream = File.OpenWrite(tempFile);
        await file.CopyToAsync(stream);
    }
});

app.Run();

인증된 파일 업로드 요청은 인증 헤더, 클라이언트 인증서 또는 cookie 헤더를 사용하여 지원됩니다.

위조 방지의 기본 제공 지원은 없습니다. 그러나 IAntiforgery 서비스를 사용하여 구현할 수 있습니다.

[AsParameters] 특성은 인수 목록에 매개 변수 바인딩을 사용하도록 설정합니다

[AsParameters] 특성은 인수 목록에 대한 매개 변수 바인딩을 사용하도록 설정합니다. 자세한 내용은 [AsParameters]를 사용한 인수 목록에 대한 매개변수 바인딩을 참조하세요.

최소 API 및 API 컨트롤러

새 문제 세부 정보 서비스

문제 세부 정보 서비스는 IProblemDetailsServiceHTTP API에 대한 문제 세부 정보 작성을 지원하는 인터페이스를 제공합니다.

자세한 내용은 문제 세부 정보 서비스를 참조하세요.

경로 그룹

MapGroup확장 메서드는 공통 접두사를 사용하여 엔드포인트 그룹을 구성하는 데 도움이 됩니다. 반복 코드를 줄이고 엔드포인트 메타데이터를 추가하는 RequireAuthorization이나 WithMetadata와 같은 메서드로서 단일 호출로 엔드포인트 전체 그룹을 사용자 지정할 수 있게 됩니다.

예를 들어 다음 코드에서는 두 개의 유사한 엔드포인트 그룹을 만듭니다.

app.MapGroup("/public/todos")
    .MapTodosApi()
    .WithTags("Public");

app.MapGroup("/private/todos")
    .MapTodosApi()
    .WithTags("Private")
    .AddEndpointFilterFactory(QueryPrivateTodos)
    .RequireAuthorization();


EndpointFilterDelegate QueryPrivateTodos(EndpointFilterFactoryContext factoryContext, EndpointFilterDelegate next)
{
    var dbContextIndex = -1;

    foreach (var argument in factoryContext.MethodInfo.GetParameters())
    {
        if (argument.ParameterType == typeof(TodoDb))
        {
            dbContextIndex = argument.Position;
            break;
        }
    }

    // Skip filter if the method doesn't have a TodoDb parameter.
    if (dbContextIndex < 0)
    {
        return next;
    }

    return async invocationContext =>
    {
        var dbContext = invocationContext.GetArgument<TodoDb>(dbContextIndex);
        dbContext.IsPrivate = true;

        try
        {
            return await next(invocationContext);
        }
        finally
        {
            // This should only be relevant if you're pooling or otherwise reusing the DbContext instance.
            dbContext.IsPrivate = false;
        }
    };
}
public static RouteGroupBuilder MapTodosApi(this RouteGroupBuilder group)
{
    group.MapGet("/", GetAllTodos);
    group.MapGet("/{id}", GetTodo);
    group.MapPost("/", CreateTodo);
    group.MapPut("/{id}", UpdateTodo);
    group.MapDelete("/{id}", DeleteTodo);

    return group;
}

이 시나리오에서는 201 Created 결과에서 Location 헤더에 대한 상대 주소를 사용할 수 있습니다.

public static async Task<Created<Todo>> CreateTodo(Todo todo, TodoDb database)
{
    await database.AddAsync(todo);
    await database.SaveChangesAsync();

    return TypedResults.Created($"{todo.Id}", todo);
}

첫 번째 엔드포인트 그룹은 접두사로 지정된 요청만 /public/todos 매칭하게 되며 인증 없이 액세스할 수 있습니다. 두 번째 엔드포인트 그룹은 접두사 /private/todos를 가지고 인증이 필요한 요청만 일치합니다.

QueryPrivateTodos엔드포인트 필터 팩터리는 경로 처리기의 TodoDb 매개 변수를 수정하여 프라이빗 할일 데이터에 접근하고 보관할 수 있도록 수정하는 로컬 함수입니다.

경로 그룹은 경로 매개 변수 및 제약 조건이 있는 중첩 그룹 및 복잡한 접두사 유형도 지원합니다. 다음 예제에서 그룹에 매핑된 user 경로 처리기는 외부 그룹 접두사에 정의된 {org}{group} 경로 매개 변수를 캡처할 수 있습니다.

접두사는 비어 있을 수도 있습니다. 이 기능은 경로 패턴을 변경하지 않고 엔드포인트 메타데이터 또는 필터를 엔드포인트 그룹에 추가하는 데 유용할 수 있습니다.

var all = app.MapGroup("").WithOpenApi();
var org = all.MapGroup("{org}");
var user = org.MapGroup("{user}");
user.MapGet("", (string org, string user) => $"{org}/{user}");

그룹에 필터 또는 메타데이터를 추가하면 내부 그룹 또는 특정 엔드포인트에 추가될 수 있는 추가 필터 또는 메타데이터를 추가하기 전에 각 엔드포인트에 개별적으로 추가하는 것과 동일한 결과가 됩니다.

var outer = app.MapGroup("/outer");
var inner = outer.MapGroup("/inner");

inner.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/inner group filter");
    return next(context);
});

outer.AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("/outer group filter");
    return next(context);
});

inner.MapGet("/", () => "Hi!").AddEndpointFilter((context, next) =>
{
    app.Logger.LogInformation("MapGet filter");
    return next(context);
});

위의 예제에서 외부 필터는 두 번째로 추가된 경우에도 내부 필터 앞에 들어오는 요청을 기록하게 됩니다. 필터가 서로 다른 그룹에 적용되었기 때문에 필터가 추가된 상대적 순서는 중요하지 않습니다. 동일한 그룹 또는 특정 엔드포인트에 적용되는 경우 주문 필터가 추가됩니다.

/outer/inner/에 대한 요청은 다음을 기록합니다.

/outer group filter
/inner group filter
MapGet filter

gRPC

JSON 코드 변환

gRPC RESTON 코드 변환은 gRPC 서비스용 JSful JSON API를 만드는 ASP.NET Core를 위한 확장입니다. gRPC JSON 코드 변환을 사용하면 다음이 가능합니다.

  • 앱에서 친숙한 HTTP 개념으로 gRPC 서비스를 호출할 수 있습니다.
  • ASP.NET Core gRPC 앱은 기능을 복제하지 않고 gRPC 및 RESTful JSON API를 지원합니다.
  • Swashbuckle과 통합하여 코드 변환된 RESTful API에서 OpenAPI를 생성하기 위한 실험적 기능 지원.

자세한 내용은 ASP.NET Core gRPC 앱에서 gRPC JSON 코드 변환 및 gRPCON 코드 변환JS과 ASP.NET Core 앱에서 OpenAPI 사용을 참조하세요.

ASP.NET Core의 gRPC 상태 검사

gRPC 상태 검사 프로토콜은 gRPC 서버 앱의 상태를 보고하기 위한 표준입니다. 앱은 gRPC 서비스로서 상태 검사를 노출합니다. 일반적으로 외부 모니터링 서비스와 함께 앱의 상태를 확인하는 데 사용됩니다.

gRPC ASP.NET Core는 Grpc.AspNetCore.HealthChecks 패키지에서 gRPC 상태 검사를 기본적으로 지원합니다. .NET 상태 검사의 결과는 호출자에게 보고됩니다.

자세한 내용은 ASP.NET Core의 상태 검사을 참조하세요.

향상된 호출 자격 증명 지원

호출 자격 증명은 인증 토큰을 서버에 보내도록 gRPC 클라이언트를 구성하는데 있어 권장되는 방법입니다. gRPC 클라이언트는 호출 자격 증명을 더 쉽게 사용할 수 있도록 두 가지 새로운 기능을 지원합니다.

  • 일반 텍스트 연결을 사용하여 호출 자격 증명을 지원합니다. 이전에는 연결이 TLS로 보호된 경우에만 gRPC 호출에서 호출 자격 증명을 보냈습니다. UnsafeUseInsecureChannelCallCredentials라는 새로운 설정을 GrpcChannelOptions에서 사용하면 이 동작을 사용자 지정할 수 있습니다. TLS와의 연결을 보호하지 아니하는 경우 보안에 영향을 미칩니다.
  • AddCallCredentials이라는 새로운 매서드를 gRPC 클라이언트 팩터리에서 사용할 수 있습니다. AddCallCredentials는 gRPC 클라이언트에 대한 호출 자격 증명을 구성하는 빠른 방법이며 DI(종속성 주입)와 잘 통합됩니다.

다음 코드는 Authorization 메타데이터를 보내도록 gRPC 클라이언트 팩터리를 구성합니다.

builder.Services
    .AddGrpcClient<Greeter.GreeterClient>(o =>
    {
       o.Address = new Uri("https://localhost:5001");
    })
    .AddCallCredentials((context, metadata) =>
    {
       if (!string.IsNullOrEmpty(_token))
       {
          metadata.Add("Authorization", $"Bearer {_token}");
       }
       return Task.CompletedTask;
    });

자세한 내용은 gRPC 클라이언트 팩터리를 사용하여 전달자 토큰 구성을 참조하세요.

SignalR

클라이언트 결과

이제 서버는 클라이언트의 결과 요청을 지원합니다. 이렇게 하려면 서버가 ISingleClientProxy.InvokeAsync를 사용하고 클라이언트는 .On 처리기에서 결과를 반환해야 합니다. 강력한 형식의 허브는 인터페이스 메서드에서 값을 반환할 수도 있습니다.

자세한 내용은 클라이언트 결과를 참조하세요.

SignalR 허브 메서드에 대한 종속성 주입

SignalR 허브 메서드는 이제 DI(종속성 주입)를 통해 서비스 주입을 지원합니다.

허브 생성자는 DI의 서비스를 매개 변수로 수락할 수 있으며, 허브 메서드에서 사용하기 위해 클래스의 속성에 저장할 수 있습니다. 자세한 내용은 허브에 서비스 삽입을 참조하세요.

Blazor

위치 변경 이벤트 및 탐색 상태 처리

.NET 7에서 Blazor는 위치 변경 이벤트 및 탐색 상태 유지 관리를 지원합니다. 이렇게 하면 저장되지 않은 작업에 대해 사용자에게 경고하거나 사용자가 페이지 탐색을 수행할 때 관련 작업을 수행할 수 있습니다.

자세한 내용은 라우팅 및 탐색 문서의 다음 섹션을 참조하세요.

빈 Blazor 프로젝트 템플릿

Blazor에서는 빈 슬레이트에서 시작하는 두 개의 새 프로젝트 템플릿이 있습니다. 새 Blazor Server빈 앱Blazor WebAssembly빈 앱 프로젝트 템플릿은 비어 있지 않은 것과 비슷하지만 예제 코드는 없습니다. 이러한 빈 템플릿에는 기본 홈페이지만 포함되며 다른 CSS 프레임워크로 시작할 수 있도록 부트스트랩을 제거하였습니다.

자세한 내용은 다음 문서를 참조하세요.

Blazor 사용자 지정 요소

Microsoft.AspNetCore.Components.CustomElements 패키지를 통해 Blazor를 사용하여 표준 기반 사용자 지정 DOM 요소를 빌드할 수 있습니다.

자세한 내용은 ASP.NET Core Razor 구성 요소를 참조하세요.

바인딩 한정자(@bind:after, @bind:get, @bind:set)

Important

@bind:after/@bind:get/@bind:set 기능은 현재 추가 업데이트를 받고 있습니다. 최신 업데이트를 활용하려면, 최신 SDK를 설치했는지 확인합니다.

이벤트 콜백 매개 변수([Parameter] public EventCallback<string> ValueChanged { get; set; }) 사용은 지원되지 않습니다. 그 대신 @bind:set/@bind:afterAction-returning 혹은 Task-returning 메서드를 전달하세요.

자세한 내용은 다음 리소스를 참조하세요.

.NET 7에서는 새 @bind:after 한정자를 사용하여 바인딩 이벤트가 완료된 후 비동기 논리를 실행할 수 있습니다. 다음 예제에서는 검색 텍스트에 PerformSearch 대한 변경 내용이 검색된 후 비동기 메서드가 자동으로 실행됩니다.

<input @bind="searchText" @bind:after="PerformSearch" />

@code {
    private string searchText;

    private async Task PerformSearch()
    {
        ...
    }
}

.NET 7에서는 구성 요소 매개 변수에 대한 바인딩을 설정하는 것이 더 쉽습니다. 구성 요소는 매개 변수 쌍을 정의하여 양방향 데이터 바인딩을 지원할 수 있습니다.

  • @bind:get: 바인딩할 값을 지정합니다.
  • @bind:set: 값이 변경되는 경우에 대한 콜백을 지정합니다.

@bind:get@bind:set 한정자는 항상 함께 사용됩니다.

예:

@* Elements *@

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

<input type="text" @bind="text" @bind:after="() => { }" />

<input type="text" @bind:get="text" @bind:set="(value) => { }" />

<input type="text" @bind="text" @bind:after="AfterAsync" />

<input type="text" @bind:get="text" @bind:set="SetAsync" />

@* Components *@

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

<InputText @bind-Value="text" @bind-Value:after="() => { }" />

<InputText @bind-Value:get="text" @bind-Value:set="(value) => { }" />

<InputText @bind-Value="text" @bind-Value:after="AfterAsync" />

<InputText @bind-Value:get="text" @bind-Value:set="SetAsync" />

@code {
    private string text = "";

    private void After(){}
    private void Set() {}
    private Task AfterAsync() { return Task.CompletedTask; }
    private Task SetAsync(string value) { return Task.CompletedTask; }
}

구성 요소에 대한 InputText 자세한 내용은 ASP.NET Core Blazor 입력 구성 요소를 참조하세요.

핫 다시 로드 개선 사항

.NET 7에서 핫 다시 로드 지원에는 다음이 포함됩니다.

  • 구성 요소는 값이 제거될 때 매개 변수를 기본값으로 다시 설정합니다.
  • Blazor WebAssembly:
    • 새 형식을 추가합니다.
    • 중첩 클래스를 추가합니다.
    • 기존 형식에 정적 및 인스턴스 메서드를 추가합니다.
    • 기존 형식에 정적 필드 및 메서드를 추가합니다.
    • 기존 메서드에 정적 람다를 추가합니다.
    • 이전에 이미 this를 캡처한 기존 메서드에 this를 캡처하는 람다를 추가합니다.

Blazor WebAssembly에서 MSAL을 사용한 동적 인증 요청

.NET 7의 새로운 기능인 Blazor WebAssembly는 고급 인증 시나리오를 처리하기 위해 사용자 지정 매개 변수를 사용하여 런타임에 동적 인증 요청 생성을 지원합니다.

자세한 내용은 다음 문서를 참조하세요.

Blazor WebAssembly 디버깅 기능 개선

Blazor WebAssembly 디버깅에는 다음과 같은 개선 사항이 있습니다.

  • 사용자 코드가 아닌 형식 멤버를 표시하거나 숨기는 내 코드만 설정이 지원됩니다.
  • 다차원 배열 검사를 지원합니다.
  • 호출 스택은 이제 비동기 메서드에 대한 올바른 이름을 표시합니다.
  • 언어 식 평가가 향상되었습니다.
  • 파생 멤버에서 new 키워드를 올바르게 처리합니다.
  • System.Diagnostics의 디버거 관련 특성 지원

System.Security.Cryptography WebAssembly에 대한 지원

.NET 6은 WebAssembly에서 실행할 때 해시 알고리즘의 SHA 제품군을 지원했습니다. .NET 7을 사용하면 가능하면 SubtleCrypto를 활용하고 SubtleCrypto를 사용할 수 없는 경우 .NET 구현으로 대체하여 더 많은 암호화 알고리즘을 사용할 수 있습니다. 다음 알고리즘은 .NET 7의 WebAssembly에서 지원됩니다.

  • SHA1
  • SHA256
  • SHA384
  • SHA512
  • Hmacsha1
  • HMACSHA256
  • Hmacsha384
  • HMACSHA512
  • AES-CBC
  • PBKDF2
  • HKDF

자세한 내용은 browser-wasm을 대상으로 하는 개발자가 웹 Crypto API를 사용할 수 있음(dotnet/runtime #40074)을 참조하세요.

사용자 지정 유효성 검사 특성에 서비스 삽입

이제 서비스를 사용자 지정 유효성 검사 특성에 삽입할 수 있습니다. Blazor는 서비스 공급자로 사용할 수 있도록 ValidationContext를 설정합니다.

자세한 내용은 ASP.NET Core Blazor 양식 유효성 검사를 참조하세요.

EditContext/EditForm 외부의 구성 요소Input*

이제 기본 제공 입력 구성 요소는 Razor 구성 요소 표시 형식 외부에서 지원됩니다.

자세한 내용은 ASP.NET Core Blazor 입력 구성 요소를 참조하세요.

프로젝트 템플릿 변경 사항

작년에 .NET 6이 릴리스되었을 때 페이지의 HTML 태그_Host(Pages/_Host.chstml)가 .NET 6 _Host 프로젝트 템플릿의 _Layout 페이지와 새 Pages/_Layout.chstml 페이지(Blazor Server)로 분할되었습니다.

.NET 7에서는 HTML 태그가 프로젝트 템플릿의 _Host 페이지와 다시 결합되었습니다.

프로젝트 템플릿에 Blazor에 대한 몇 가지 추가 변경이 적용되었습니다. 설명서의 템플릿에 대한 모든 변경 내용을 나열하는 것은 어렵습니다. 모든 변경 내용을 채택하기 위해 앱을 .NET 7로 마이그레이션하려면 ASP.NET Core 6.0에서 7.0으로 마이그레이션을 참조하세요.

실험적 QuickGrid 구성 요소

QuickGrid 구성 요소는 가장 일반적인 요구 사항 및 Blazor 데이터 그리드 구성 요소를 빌드하는 모든 사용자를 위한 참조 아키텍처 및 성능 기준선으로 편리한 데이터 그리드 구성 요소를 제공합니다.

자세한 내용은 ASP.NET Core Blazor QuickGrid 구성 요소를 참조하세요.

라이브 데모: Blazor 샘플 앱 용 QuickGrid

가상화 개선 사항

.NET 7의 가상화 개선 사항:

  • Virtualize 구성 요소는 overflow-y: scroll이 적용된 다른 요소를 사용하는 대신 문서 자체를 스크롤 루트로 사용할 수 있도록 지원합니다.
  • Virtualize 구성 요소가 특정 자식 태그 이름이 필요한 요소 내에 배치된 경우 SpacerElement에서 가상화 스페이서 태그 이름을 가져오거나 설정할 수 있습니다.

자세한 내용은 가상화 문서의 다음 섹션을 참조하세요.

MouseEventArgs 업데이트

MovementXMovementYMouseEventArgs에 추가되었습니다.

자세한 내용은 ASP.NET Core Blazor 이벤트 처리를 참조하세요.

새 Blazor 로드 페이지

Blazor WebAssembly 프로젝트 템플릿에 이제 앱 로딩 진행률을 보여주는 새로운 로딩 UI를 지원합니다.

자세한 내용은 ASP.NET Core Blazor 시작을 참조하세요.

Blazor WebAssembly에서 인증에 대한 진단 개선

Blazor WebAssembly 애플리케이션 인증 문제를 진단하기 위해 자세한 로깅을 사용할 수 있습니다.

자세한 내용은 ASP.NET CoreBlazor 로깅을 참조하세요.

WebAssembly의 JavaScript interop

JavaScript [JSImport]/[JSExport] interop API는 및 JavaScript 기반 앱에서 Blazor WebAssembly .NET을 사용하기 위한 새로운 하위 수준 메커니즘입니다. 이 새로운 JavaScript interop 기능을 사용하면 .NET WebAssembly 런타임을 사용하여 JavaScript에서 .NET 코드를 호출하고 Blazor UI 구성 요소 모델에 대한 종속성 없이 .NET에서 JavaScript 기능을 호출할 수 있습니다.

자세한 내용은 다음에서 확인합니다.

인증 상태 공급자의 조건부 등록

.NET 7이 릴리스되기 전에 AuthenticationStateProviderAddScoped와 함께 서비스 컨테이너에 등록되었습니다. 이로 인해 사용자 지정 구현을 제공할 때 특정 서비스 등록 순서가 강제로 적용되므로 앱을 디버그하기가 어려웠습니다. 시간이 지남에 따라 내부 프레임워크가 변경되어 더 이상 AuthenticationStateProviderAddScoped에 등록할 필요가 없습니다.

개발자 코드에서 인증 상태 공급자 서비스 등록을 다음과 같이 변경합니다.

- builder.Services.AddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();
+ builder.Services.TryAddScoped<AuthenticationStateProvider, ExternalAuthStateProvider>();

앞의 예제에서 ExternalAuthStateProvider는 개발자의 서비스 구현입니다.

.NET WebAssembly 빌드 도구 개선

성능 개선 및 예외 핸들에 도움이 되는.NET 7 wasm-tools 워크로드의 새로운 기능

자세한 내용은 ASP.NET Core Blazor WebAssembly 빌드 도구 및 AOT(Ahead-Of-Time) 컴파일을 참조하세요.

Blazor Hybrid

외부 URL

브라우저에서 외부 웹 페이지를 열 수 있는 옵션이 추가되었습니다.

자세한 내용은 ASP.NET Core Blazor Hybrid 라우팅 및 탐색을 참조하세요.

보안

새로운 지침은 Blazor Hybrid 보안 시나리오에서 사용할 수 있습니다. 자세한 내용은 다음 문서를 참조하세요.

성능

출력 캐싱 미들웨어

출력 캐싱은 웹앱의 응답을 저장하고 매번 계산하는 대신 이를 캐시에서 제공하는 새로운 미들웨어입니다. 출력 캐싱은 아래와 같이 응답 캐싱과 다릅니다.

  • 캐싱 동작은 서버에서 구성할 수 있습니다.
  • 캐시 항목은 프로그래밍 방식으로 무효화될 수 있습니다.
  • 리소스 잠금을 통해 캐시 스탬프썬더링 무리 위험을 완화할 수 있습니다.
  • 캐시 유효성 재검사로서 서버가 캐시된 응답 본문 대신 304 Not Modified HTTP 상태 코드를 반환할 수 있게 됩니다.
  • 캐시 저장소 매체는 확장할 수 있습니다.

자세한 내용은 캐싱 개요출력 캐싱 미들웨어를 참조하세요.

HTTP/3 개선 사항

이 릴리스는 다음과 같습니다.

  • HTTP/3은 ASP.NET Core에서 완전히 지원하므로 더 이상 실험적 기능이 아닙니다.
  • HTTP/3에서 Kestrel 지원을 개선합니다. 주요 개선 부분은 HTTP/1.1 및 HTTP/2의 기능 패리티와 성능입니다.
  • HTTP/3에 대한 UseHttps(ListenOptions, X509Certificate2) 전체 지원을 제공합니다. Kestrel에서는 연결 인증서를 구성하기 위한 고급 옵션(예: SNI(서버 이름 표시)에 연결)을 제공합니다.
  • HTTP.sysIIS에서 HTTP/3에 대한 지원을 추가합니다.

다음 예제에서는 SNI 콜백을 사용하여 TLS 옵션을 확인하는 방법을 보여줍니다.

using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.Server.Kestrel.Https;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
    options.ListenAnyIP(8080, listenOptions =>
    {
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps(new TlsHandshakeCallbackOptions
        {
            OnConnection = context =>
            {
                var options = new SslServerAuthenticationOptions
                {
                    ServerCertificate = 
                         MyResolveCertForHost(context.ClientHelloInfo.ServerName)
                };
                return new ValueTask<SslServerAuthenticationOptions>(options);
            },
        });
    });
});

HTTP/3 할당을 줄이기 위해 .NET 7에서 상당한 작업을 수행하였습니다. 다음 GitHub PR에서 이러한 개선 사항 중 일부를 볼 수 있습니다.

HTTP/2 성능 개선 사항

.NET 7에는 Kestrel에서 HTTP/2 요청을 처리하는 방법에 대한 중요한 재설계가 도입되었습니다. 사용량이 많은 HTTP/2 연결을 사용하는 ASP.NET Core 앱은 CPU 사용량이 감소하고 처리량이 증가합니다.

이전에는 HTTP/2 멀티플렉싱 구현에서 잠금 제어를 활용했으며, 요청에서 기본 TCP 연결에 쓸 수 있습니다. 스레드로부터 안전한 대기열이 쓰기 잠금을 대체합니다. 이제 쓰기 잠금을 사용할 스레드를 두고 경합하는 대신 요청이 큐에 대기하고 전용 소비자가 이를 처리합니다. 이전에 낭비되었던 CPU 리소스를 앱의 나머지 부분에 사용할 수 있습니다.

이러한 향상된 기능을 확인할 수 있는 한 곳은 HTTP/2를 사용하는 인기 RPC 프레임워크인 gRPC입니다. Kestrel + gRPC 벤치마크는 크게 향상되었습니다.

gRPC 서버 스트리밍 성능

단일 HTTP/2 연결에서 데이터를 쓰려는 여러 스트림이 있을 때 성능을 향상시키는 HTTP/2 프레임 작성 코드를 변경하였습니다. 이제 TLS 작업을 스레드 풀로 제공하고 다른 스트림이 자신의 데이터를 작성하기 위해 획득할 수 있는 쓰기 잠금을 더 빠르게 해제합니다. 대기 시간이 감소하면 이러한 쓰기 잠금에 대한 경합이 있는 경우 성능이 크게 향상될 수 있습니다. 단일 연결(TLS 포함)에 70개의 스트림이 있는 gRPC 벤치마크에서 이 변경으로 RPS(초당 요청 수)가 15% 향상됨을 보였습니다.

HTTP/2 WebSockets 지원

.NET 7에서는 Kestrel, SignalR JavaScript 클라이언트, Blazor WebAssembly를 사용한 SignalR 등에 대한 Websockets over HTTP/2 지원을 도입했습니다.

WebSockets over HTTP/2를 사용하면 다음과 같은 새로운 기능을 활용할 수 있습니다.

  • 헤더 압축.
  • 서버에 대한 여러 요청을 수행할 때 필요한 시간과 리소스를 줄이는 멀티플렉싱.

이 지원되는 기능은 모든 HTTP/2 지원 플랫폼의 Kestrel에서 사용할 수 있습니다. 버전 협상은 브라우저 및 Kestrel에서 자동이므로 새 API가 필요하지 않습니다.

자세한 내용은 Http/2 WebSockets 지원을 참조하세요.

상위 코어 머신의 Kestrel 성능 개선

Kestrel은 ConcurrentQueue<T>를 여러 용도로 사용합니다. 한 가지 목적은 Kestrel의 기본 소켓 전송에서 I/O 작업을 예약하는 것입니다. 연결된 소켓에 따라 ConcurrentQueue를 분할하면 경합이 줄어들고 CPU 코어가 많은 머신에서 처리량이 증가합니다.

.NET 6의 상위 코어 머신에서 프로파일링을 수행하면 Kestrel의 다른 ConcurrentQueue 인스턴스 중 하나(Kestrel에서 바이트 버퍼를 캐시하는 데 사용하는 PinnedMemoryPool)에서 상당한 경합이 나타났습니다.

.NET 7에서 Kestrel의 메모리 풀은 I/O 큐와 동일한 방식으로 분할되어 높은 코어 머신에서 훨씬 더 낮은 경합 및 높은 처리량을 발생합니다. 80개의 코어 ARM64 VM에서 TechEmpower 일반 텍스트 벤치마크에서 RPS(초당 응답 수)가 500% 이상 향상되었습니다. 48개의 코어 AMD VM에서는 HTTPS JSON 벤치마크에서 성능이 거의 100% 향상되었습니다.

시작 시간을 측정하는 ServerReady 이벤트

EventSource를 사용하는 앱은 시작 시간을 측정하여 시작 성능을 이해하고 최적화할 수 있습니다. Microsoft.AspNetCore.Hosting의 새 ServerReady 이벤트는 서버가 요청에 응답할 준비가 된 지점을 나타냅니다.

서버

시작 시간을 측정하기 위한 새 ServerReady 이벤트

ServerReady 이벤트가 추가되어 ASP.NET Core 앱의 시작 시간을 측정합니다.

IIS

IIS의 섀도 복사

IIS용 ANCM(ASP.NET Core 모듈)에 앱 어셈블리를 섀도 복사하면 앱 오프라인 파일을 배포하여 앱을 중지하는 것보다 더 나은 최종 사용자 환경을 제공할 수 있습니다.

자세한 내용은 IIS의 섀도 복사를 참조하세요.

기타

Kestrel 전체 인증서 체인 개선 사항

HttpsConnectionAdapterOptions에는 X509Certificate2Collection 형식의 새 ServerCertificateChain 속성이 있으므로 중간 인증서를 포함한 전체 체인을 지정하여 인증서 체인의 유효성을 검사하는 것이 더욱 쉽게 되었습니다. 자세한 내용은 dotnet/aspnetcore#21513 참조하세요.

dotnet watch

dotnet watch에 대한 향상된 콘솔 출력

dotnet watch의 콘솔 출력은 ASP.NET Core의 로깅에 더 잘 맞고 😮이미지😍로 눈에 띄게 개선되었습니다.

새 출력의 예는 다음과 같습니다.

dotnet watch에 대한 출력

자세한 내용은 이 GitHub 끌어오기 요청을 참조하세요.

편집 다시 실행에 대해 항상 다시 시작하도록 dotnet watch 구성

편집 다시 실행은 핫 다시 로드할 수 없는 편집입니다. 편집 다시 실행에 대한 프롬프트 없이 항상 다시 시작되도록 dotnet watch를 구성하려면 DOTNET_WATCH_RESTART_ON_RUDE_EDIT 환경 변수를 true로 설정합니다.

개발자 예외 페이지 다크 모드

Patrick Westerhoff의 도움을 통해 다크 모드 지원이 개발자 예외 페이지에 추가되었습니다. 브라우저에서 다크 모드를 테스트하려면 개발자 도구 페이지에서 모드를 다크로 설정합니다. 예를 들어 Firefox에서:

F12로 FF 다크 모드 사용

Chrome에서:

F12로 Chrome 다크 모드 사용

최상위 문 대신 Program.Main 메서드를 사용하는 프로젝트 템플릿 옵션

.NET 7 템플릿에는 최상위 문을 사용하지 않고 Program 클래스에 선언된 Main 메서드 및 namespace를 생성하는 옵션이 포함되어 있습니다.

.NET CLI를 사용하여 --use-program-main 옵션을 사용합니다.

dotnet new web --use-program-main

Visual Studio에서는 프로젝트를 만드는 동안 새 최상위 문 사용 안 함 확인란을 선택합니다.

확인란

업데이트된 Angular 및 React 템플릿

Angular 프로젝트 템플릿은 Angular 14로 업데이트되었습니다. React 프로젝트 템플릿은 React 18.2로 업데이트되었습니다.

dotnet user-jwts를 사용하여 개발 중인 JSON Web Token 관리

새로운 dotnet user-jwts 명령줄 도구는 앱별 로컬 JWT(JSON Web Token)을 만들고 관리할 수 있습니다. 자세한 내용은 dotnet user-jwts를 사용하여 개발 중인 JSON Web Token 관리를 참조하세요.

W3CLogger에서 추가 요청 헤더 지원

이제 W3CLoggerOptions에서 AdditionalRequestHeaders()를 호출하여 W3C 로거를 사용할 때 기록할 추가 요청 헤더를 지정할 수 있습니다.

services.AddW3CLogging(logging =>
{
    logging.AdditionalRequestHeaders.Add("x-forwarded-for");
    logging.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
});

자세한 내용은 W3CLogger 옵션을 참조하세요.

요청 압축 해제

요청 압축 해제 미들웨어:

  • API 엔드포인트가 압축된 콘텐츠로 요청을 수락할 수 있도록 합니다.
  • Content-Encoding HTTP 헤더를 사용하여 압축된 콘텐츠를 포함하는 요청을 자동으로 식별하고 압축을 해제합니다.
  • 압축된 요청을 처리하는 코드를 작성할 필요가 없습니다.

자세한 내용은 요청 압축 해제 미들웨어를 참조하세요.