Среди улучшений ASP.NET Core поддержка фильтров обработчиков маршрутов Minimal API, повышение тестируемости обработчиков маршрутов Minimal API, биндинг в контроллерах MVC и контроллерах API с помощью TryParse и не только. Материалом из блога разработчиков делимся к старту курса по разработке на C#.


Чтобы больше узнать о работе над ASP.NET Core для .NET 7, смотрите план развития ASP.NET Core для .NET 7 на GitHub.

Начнём

Загрузить SDK .NET 7 можно здесь.

Если вы на Windows и работаете с Visual Studio, мы рекомендуем установить последнюю предварительную версию Visual Studio 2022. На Visual Studio версии для Mac поддержки .NET 7 Preview 3 пока нет, но скоро она появится.

Чтобы установить последние инструменты сборки .NET WebAssembly,  выполните эту команду с повышенными привилегиями:

dotnet workload install wasm-tools

Cоздание проектов .NET 6 Blazor с помощью SDK .NET 7 и инструментов сборки .NET 7 WebAssembly пока не поддерживается. Мы исправим это в обновлении .NET 7.

Обновление существующего проекта

Чтобы обновить существующее приложение ASP.NET Core с .NET 7 Preview 2 до .NET 7 Preview 3, измените:

  • все ссылки на пакеты Microsoft.AspNetCore.* — до версии 7.0.0-preview.3.*;

  • все ссылки на пакеты Microsoft.Extensions.* — до версии 7.0.0-preview.3.*;

Список изменений, ломающих обратную совместимость, смотрите по ссылке.

Поддержка фильтров обработчиков маршрутов в Minimal API

Теперь в обработчиках маршрутов приложений Minimal поддерживаются фильтры, выполняются они перед выполнением основной логики обработчика маршрута. Эти фильтры можно использовать, чтобы проверить и изменить обработчик, а также перехватить его выполнение. Регистрируются эти фильтры по-разному. К примеру, через RouteHandlerFilterDelegate и метод расширения AddFilter:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter((context, next) =>
    {
        var name = (string) context.Parameters[0];
        if (name == "Bob")
        {
            return Result.Problem("No Bob's allowed");
        }
        return next(context);
    });

Или с помощью стратегии фабрики фильтров, которая открывает доступ к RouteHandlerContext. Последний, в свою очередь, предоставляет доступ к связанному с обработчиком MethodInfo, а также к метаданным, зарегистрированным в конечной точке:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter((routeHandlerContext, next) =>
    {
        var parameters = routeHandlerContext.GetParameters();
        var hasCorrectSignature = parameters.Length == 1 && parameters[0].GetType() == typeof(string);
        return (context) =>
        {
            if (hasCorrectSignature)
            {
                var name = (string) context.Parameters[0];
                if (name == "Bob")
                {
                    return Result.Problem("No Bob's allowed");
                }
            }
            return next(context);
        }
    });

Фильтры могут реализовывать интерфейс IRouteHandlerFilter, разрешаться из инъекции зависимостей или передаваться в качестве экземпляра:

app.MapGet("/hello/{name}", (string name) => $"Hello, {name}!")
    .AddFilter<MyFilter>();

Повышена тестируемость обработчиков маршрутов Minimal API

Типы реализации IResult с суффиксом HttpResult (OkObjectHttpResult, ProblemHttpResult и так далее) теперь открыты в пространстве имён Microsoft.AspNetCore.Http. Благодаря этим типам проще тестировать обработчики маршрутов Minimal API, вместо лямбда-выражений используя именованные методы:

[Fact]
public async Task GetTodoReturnsTodoFromDatabase()
{
    var todo = new Todo { Id = 42, Name = "Improve Results testability!" };
    var mockDb = new MockTodoDb(new[] { todo });

    var result = (OkObjectHttpResult)await TodoEndpoints.GetTodo(mockDb, todo.Id);

    //Assert
    Assert.Equal(200, result.StatusCode);

    var foundTodo = Assert.IsAssignableFrom<Models.Todo>(result.Value);
    Assert.Equal(id, foundTodo.Id);
}

[Fact]
public void CreateTodoWithValidationProblems()
{
    //Arrange
    var newTodo = default(Todo);
    var mockDb = new MockTodoDb();

    //Act
    var result = TodoEndpoints.CreateTodo(mockDb, newTodo);

    //Assert        
    var problemResult = Assert.IsAssignableFrom<ProblemHttpResult>(result);
    Assert.NotNull(problemResult.ProblemDetails);
    Assert.Equal(400, problemResult.StatusCode);
}

Биндинг с помощью TryParse в контроллерах MVC и контроллерах API

Значения параметров действия контроллера теперь можно привязывать с помощью метода TryParse с одной из этих сигнатур:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

Ниже действие Get привязывает данные из строки запроса через метод типа-параметра TryParse:

public class TryParseController : ControllerBase
{
    // GET /tryparse?data=MyName
    [HttpGet]
    public ActionResult Get([FromQuery]CustomTryParseObject data) => Ok();

    public class CustomTryParseObject
    {
        public string? Name { get; set; }

        public static bool TryParse(string s, out CustomTryParseObject result)
        {
            if (s is null) 
            {
                result = default;
                return false;
            }

            result = new CustomTryParseObject { Name = s };
            return true;
        }
    }
}

Новые перегрузки Results.Stream()

Для ситуаций, когда к базовому потоку ответов HTTP нужен доступ без буферизации, написаны новые перегрузки Results.Stream(...). Эти перегрузки упрощают передачу данных в поток ответов HTTP через API, например из хранилища BLOB-объектов Azure. Ниже Results.Stream() используется в обработке изображений через сервис ImageSharp:

app.MapGet("/process-image", async (HttpContext http) =>
{
    using var image = await Image.LoadAsync("puppy.jpeg");
    int width = image.Width / 2;
    int height = image.Height / 2;
    image.Mutate(x => x.Resize(width, height));
    http.Response.Headers.CacheControl = $"public,max-age={FromHours(24).TotalSeconds}";
    return Results.Stream(stream => image.SaveAsync(stream, PngFormat.Instance), "image/png");
});

Повышена производительность соединения HTTP/2 с несколькими стримами

Мы изменили код записи фреймов HTTP/2. Этот код повышает производительность при наличии нескольких стримов, пытающихся записать данные в одном соединении HTTP/2. Теперь работа TLS отправляется в пул потоков.

Блокировка записи, которую другие стримы могут получить для записи своих данных, освобождается быстрее. Когда за эту блокировку есть конкуренция, сокращение времени ожидания может значительно повысить производительность.

Тест gRPC с 70 потоками в одном соединении с TLS показал увеличение количества запросов в секунду примерно на 15%.

Новое событие измерения времени запуска приложения — ServerReady

Если для измерений или диагностики вы используете EventSource и хотите измерить время запуска своего приложения, можно использовать новое событие ServerReady в источнике Microsoft.AspNetCore.Hosting. Это событие представляет момент, когда сервер запущен и работает.

Тёмная тема страницы исключений для разработчиков

Страница исключений для разработчиков ASP.NET Core теперь поддерживает тёмную тему:

За это улучшение благодарим @poke!

Обратная связь

Надеемся, что вам понравится этот предварительный релиз ASP.NET Core в .NET 7. Сообщите нам, что вы думаете об этих улучшениях, отправив Issues на GitHub. Спасибо, что попробовали ASP.NET Core!

А мы поможем прокачать скиллы или с самого начала освоить IT-профессию, актуальную в любое время:

Выбрать другую востребованную профессию.

Краткий каталог курсов и профессий

Комментарии (2)


  1. Dr9vik
    15.04.2022 09:58
    +1

    а куда они спешат?
    я только недавно начал 6 ковырять, а тут уже 7 наподлёте
    и больше похоже на минорные изменения, назвали бы 5.2 ничего бы не изменилось


    1. ZZnOB
      15.04.2022 10:23
      +3

      Ковыряйте на здоровье, Net 6 же LTS, потом будете ковырять Net 8, пока на Net 7 можно посматривать из праздного любопытства.