.Net 7 (предварительная версия 4) — Минимальный API — Множественный тип результата — Группы маршрутов

В этой статье мы откроем для себя новые возможности, которые появятся в .Net 7 с Minimal API.

Вопросы, которые мы рассмотрим сегодня

  • Возврат нескольких типов результатов из минимальных API
  • Самодокументирующийся Todos API
  • Группы маршрутов

Вы можете посмотреть полное видео на YouTube

Вы можете найти полный исходный код на github
https://github.com/mohamadlawand087/Net7-MinimalApi-RouteGroup-MultipleResultType

Мы собираемся продолжить работу над проектом из прошлой статьи, где мы реализовали фильтры на Minimal Api, вы можете найти статью здесь

https://dev.to/moe23/net-7-preview-4-minimal-api-filters-1812/

Начало проекта GitHub

https://github.com/mohamadlawand087/Net7-MinimalApi-Filters

После регистрации мы начнем рефакторинг нашего приложения, чтобы использовать последние возможности .Net 7 preview 4.

Первое, что мы сделаем, это рефакторинг нашего существующего приложения, первой частью будет рефакторинг нашей CRUD-операции Todo.

static class TodoApiV1
{
        // Static method to integrate with the .Net middleware 
        // Build the end route to integrate the different endpoints
    public static IEndpointRouteBuilder MapTodoApi(this IEndpointRouteBuilder routes)
    {
        routes.MapGet("/v1/items", GetAllItems);
        routes.MapGet("/v1/items/{id}", GetItem);
        routes.MapPost("/v1/items", CreateItem).AddFilter<ValidationFilter<Item>>();
        routes.MapPut("/v1/items/{id}", UpdateItem).AddFilter<ValidationFilter<Item>>();
        return routes;
    }

        // Get All Items
    public static async Task<Ok<List<Item>>> GetAllItems(ApiDbContext db)
    {
        return TypedResults.Ok(await db.Items.ToListAsync());
    }

        // Get a single item
    public static async Task<Results<Ok<Item>, NotFound>> GetItem(int id, ApiDbContext db)
    {
         return await db.Items.FirstOrDefaultAsync(x => x.Id == id) is Item item
         ? TypedResults.Ok(item)
         : TypedResults.NotFound();  
    }

        // Create a new item
    public static async Task<Results<Created<Item>, BadRequest>> CreateItem(Item item, ApiDbContext db)
    {
        if (await db.Items.FirstOrDefaultAsync(x => x.Id == item.Id) != null)
        {
            return TypedResults.BadRequest();
        }

        db.Items.Add(item);
        await db.SaveChangesAsync();
        return TypedResults.Created($"/Items/{item.Id}", item);
    }

        // Update the item
    public static async Task<Results<NoContent, NotFound>> UpdateItem(Item item, int id, ApiDbContext db)
    {
        var existItem = await db.Items.FirstOrDefaultAsync(x => x.Id == id);
        if(existItem == null)
        {
            return TypedResults.NotFound();
        }

        existItem.Title = item.Title;
        existItem.IsCompleted = item.IsCompleted;

        await db.SaveChangesAsync();
        return TypedResults.NoContent();
    }
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь нам нужно обновить механизм аутентификации

static class TodoAuthentication
{
    public static IEndpointRouteBuilder MapAuthenticationAPi(this IEndpointRouteBuilder routes)
    {
        routes.MapPost("/v1/accounts/login", Login);
        return routes;
    }

    public static async Task<Results<Ok<string>, UnauthorizedHttpResult>> Login(UserDto user, IConfiguration _config)
    {
        if(user.username == "admin@mohamadlawand.com" && user.password == "Password123")
        {
            var secureKey = Encoding.UTF8.GetBytes(_config["Jwt:Key"]);

            var issuer = _config["Jwt:Issuer"];
            var audience = _config["Jwt:Audience"];
            var securityKey = new SymmetricSecurityKey(secureKey);
            var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha512);

            var jwtTokenHandler = new JwtSecurityTokenHandler();

            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = new ClaimsIdentity(new [] {
                    new Claim("Id", "1"),
                    new Claim(JwtRegisteredClaimNames.Sub, user.username),
                    new Claim(JwtRegisteredClaimNames.Email, user.username),
                    new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
                }),
                Expires = DateTime.Now.AddMinutes(5),
                Audience = audience,
                Issuer = issuer,
                SigningCredentials = credentials
            };

            var token = jwtTokenHandler.CreateToken(tokenDescriptor);
            var jwtToken = jwtTokenHandler.WriteToken(token);
            return TypedResults.Ok(jwtToken);  
        }

        return TypedResults.Unauthorized();
    }

}
Вход в полноэкранный режим Выход из полноэкранного режима

После обновления обоих механизмов нам нужно сообщить нашему промежуточному ПО о новых конечных точках

app.MapTodoApi();
app.MapAuthenticationAPi();
Вход в полноэкранный режим Выйти из полноэкранного режима

Далее нам нужно включить авторизацию на конечных точках

public static IEndpointRouteBuilder MapTodoApi(this IEndpointRouteBuilder routes)
{
    routes.MapGet("/v1/items", GetAllItems).RequireAuthorization();
    routes.MapGet("/v1/items/{id}", GetItem).RequireAuthorization();
    routes.MapPost("/v1/items", CreateItem)
                .AddFilter<ValidationFilter<Item>>()
                .RequireAuthorization();
    routes.MapPut("/v1/items/{id}", UpdateItem)
                .AddFilter<ValidationFilter<Item>>()
                .RequireAuthorization();
    return routes;
}
Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь мы собираемся включить группировку маршрутов для нашей конечной точки, первым пунктом нам нужно обновить интеграцию промежуточного ПО до следующего состояния

app.MapGroup("/v1").MapTodoApi();
app.MapGroup("/v1").MapAuthenticationAPi();
Войти в полноэкранный режим Выйти из полноэкранного режима

Далее мы обновим связку конечных точек для Todo и авторизации следующим образом

// Todo
public static GroupRouteBuilder MapTodoApi(this GroupRouteBuilder routes)
{
    routes.MapGet("/items", GetAllItems);
    routes.MapGet("/items/{id}", GetItem);
    routes.MapPost("/items", CreateItem)
                .AddFilter<ValidationFilter<Item>>();
    routes.MapPut("/items/{id}", UpdateItem)
                .AddFilter<ValidationFilter<Item>>();
    return routes;
}
Войти в полноэкранный режим Выход из полноэкранного режима
// Authorisation
public static IEndpointRouteBuilder MapAuthenticationAPi(this IEndpointRouteBuilder routes)
{
    routes.MapPost("/accounts/login", Login);
    return routes;
}
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте обновим наше промежуточное ПО

app.MapGroup("/v1").RequireAuthorization().MapCrudTodoApi();
app.MapGroup("/v1").MapAuthenticationForApi();
Войти в полноэкранный режим Выйти из полноэкранного режима

По всем вопросам обращайтесь в комментарии ниже

Оцените статью
Procodings.ru
Добавить комментарий