Как я создаю с нуля полный API с помощью .Net 7, использую внешний интерфейс с помощью Blazor и публикую в Azure Cloud сильный>!

Идея здесь состоит в том, чтобы быть очень простым проектом списка дел, ориентированным на ежедневные рутинные исследования разработчиков, где также можно реагировать на эти задачи. Надеюсь, тебе понравится!

Создание API💻

Прежде всего, я начинаю с создания основы проекта, API, который будет содержать глаголы, которые я использую для манипулирования информацией. Я использую Visual Studio Code для его кодирования, и я буду считать, что у вас есть базовые знания в области программирования и есть все предварительные условия и библиотеки для кодирования на C # и ASP.Net.

Я создаю шаблон для проекта API, используя эту команду CLI:

dotnet new webapi -o DevToDoList.API

После этого я получаю доступ к созданному каталогу preject и в каталоге Controllers я создаю DevTodosController.cs.

Этот файл будет отвечать за выполнение запросов и имеет конфигурацию маршрутов в нашем API к базе данных. Обратите внимание, что я унаследовал класс ControllerBase, очень часто используемый для построения API в структуре MVC. Также аннотация ApiController в верхней части класса имеет некоторые соглашения и упрощает некоторые вещи, такие как определение параметров.

using DevToDoList.API.Entities;
using DevToDoList.API.Models;
using DevToDoList.API.Persistence;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Models;

namespace DevToDoList.API.Controllers
{
    [ApiController]
    [Route("api/dev-todo")]
    public class DevTodosController : ControllerBase
    {
        private readonly DevTodoDbContext _context;

        public DevTodosController(DevTodoDbContext context) {
            _context = context;
        }

        [HttpGet]
        public IActionResult GetAll() {
            var devTodos = _context.DevTodos.ToList();
            return Ok(devTodos);
        }

        
        // api/dev-todo/1 HTTP GET
        [HttpGet("{id}")]
        public IActionResult GetById(int id) {
            var devTodo = _context.DevTodos
                .Include(s => s.Reactions)
                .SingleOrDefault(s => s.Id == id);

            if (devTodo == null)
                return NotFound();
            return Ok(devTodo);
        }
        
        [HttpPost]
        public IActionResult Post(DevTodoInputModel model) {
            var devTodo = new DevTodo(model.Title, model.Description, model.Done);

            _context.DevTodos.Add(devTodo);
            _context.SaveChanges();

            return CreatedAtAction("GetById", new { id = devTodo.Id }, model);
        }

        // api/dev-todo/1/reactions HTTP POST
        [HttpPost("{id}/reactions")]
        public IActionResult PostReaction(int id, ReactionDevTodoInputModel model) {
            var devTodo = _context.DevTodos.SingleOrDefault(s => s.Id == id);

            if (devTodo == null)
                return BadRequest();

            devTodo.AddReaction(model.IsPositive);

            _context.SaveChanges();
            return NoContent();
        }

    }
}

Здесь у нас есть метод HttpGet, который возвращает все задачи в базе данных, если они есть. HttpGet, который вернет только один регистр по идентификатору. HttpPost, который добавит задачу в базу данных, и другой HttpPost, который опубликует реакцию на любую из этих задач.7

Закрытая переменная _context только для чтения отвечает за обеспечение связи между методами API и базой данных. Мы поговорим об этом позже.

У нас есть эти модели в каталоге Models:
DevTodoInputModel.cs

namespace Models
{
    public class DevTodoInputModel
    {
        public string Title { get; set; }
        public string Description { get; set; }
        public bool Done { get; set; }
    }
}

ReactionDevTodoInputModel.cs

namespace DevToDoList.API.Models
{
    public class ReactionDevTodoInputModel
    {
        public int DevTodoId { get; set; }
        public bool IsPositive { get; set; }
    }
}

Теперь Entities только с данными, которые будут доступны снаружи. Это концепция безопасности, которая облегчает связь между системами. Это хорошая практика, потому что она не позволяет раскрывать конфиденциальную информацию.

DevTodo.cs

namespace DevToDoList.API.Entities
{
    public class DevTodo
    {
        public DevTodo(string title, string description, bool isDone)
        {
            Title = title;
            Description = description;
            IsDone = isDone;

            Reactions = new List<DevTodoReaction>();
            CreatedAt = DateTime.Now;
        }

        public int Id { get; private set; }
        public string Title { get; private set; }
        public string Description { get; private set; }
        public bool IsDone { get; private set; }
        public DateTime CreatedAt { get; private set; }
        public List<DevTodoReaction> Reactions { get; private set; }

        public void AddReaction(bool isPositive) {
            Reactions.Add(new DevTodoReaction(isPositive));
        
        }

    }
}

ReactionDevTodoInputModel.cs

namespace DevToDoList.API.Models
{
    public class ReactionDevTodoInputModel
    {
        public int DevTodoId { get; set; }
        public bool IsPositive { get; set; }
    }
}

Настройка базы данных 🏦

Я использовал Entity Framework Core, чтобы обеспечить постоянство базы данных данных приложения. Если вы новичок в Entity Framework, я рекомендую документацию Microsoft, чтобы помочь вам начать работу с ней.
Я использую интерфейс командной строки dotnet, поэтому для начала я запускаю эту команду, чтобы убедиться, что он распознает ее, когда я ее использую:

dotnet tool install --global dotnet-ef

Я также устанавливаю эти пакеты NuGet:

  • Microsoft.EntityFrameworkCore.InMemory
  • Microsoft.EntityFrameworkCore.SQLServer
  • Microsoft.EntityFrameworkCore.Design

Если вы находитесь в VSCode, необходимо будет установить диспетчер пакетов NuGet для установки этих пакетов.

После этого я создал директорию Persistence и в ней файл DevTodoDbContext.cs.
Этот файл важен тем, что в нем будут установлены свойства DbSet, которые будут представлять наши таблицы в базе данных вверх.

using DevToDoList.API.Entities;
using Microsoft.EntityFrameworkCore;

namespace DevToDoList.API.Persistence
{
    public class DevTodoDbContext : DbContext
    {
        public DevTodoDbContext(DbContextOptions<DevTodoDbContext> options) : base(options) {}

        public DbSet<DevTodo> DevTodos { get; set; }
        public DbSet<DevTodoReaction> DevTodoReactions { get; set; }

        protected override void OnModelCreating(ModelBuilder builder) {
            builder.Entity<DevTodo>(e => {
                e.HasKey(s => s.Id);

                e.HasMany(s => s.Reactions)
                    .WithOne()
                    .HasForeignKey(r => r.DevTodoId)
                    .OnDelete(DeleteBehavior.Restrict);
            });

            builder.Entity<DevTodoReaction>(sn => {
                sn.HasKey(s => s.Id);
            });
        }

    }
}

Обратите внимание на защищенный метод OnModelCreating, который в основном определяет первичный ключ и внешний ключ в наших таблицах.

Мы также должны добавить следующий код в файлы program.cs и appsettings.json:

appsettings.json

,
  "AllowedHosts": "*",
  "ConnectionStrings": {
    "DevTodoList": "server=yourservername; Database=DevTodoListDb; Integrated Security=True; trustServerCertificate=true"
  }

Здесь будет строка подключения к вашей базе данных.

program.cs

var connectionString = builder.Configuration.GetConnectionString("DevTodoList");

builder.Services.AddDbContext<DevTodoDbContext>(
    o => o.UseSqlServer(connectionString)
);

Здесь соединение выполняется с использованием конфигурации SqlServer.
Вся настройка завершена, теперь мы можем запустить эти команды, чтобы легко создать нашу базу данных с помощью Entity Framework Core:

dotnet ef migration add FirstMigration -o Persistence/Migrations

Это создаст нашу папку миграции и первую миграцию с командами базы данных, которые будут выполняться для создания нашей базы данных и таблиц.
Теперь мы запускаем эту команду для запуска и обновления нашей базы данных.

dotnet ef database update

Теперь наша база данных с нашими таблицами и столбцами должна быть создана, и мы можем проверить ее с помощью Management Studios или любой другой SGDB.

Документация с Swagger 📗

До сих пор мы сохраняли простоту документирования API с помощью Swagger. Но можно детализировать различные данные, такие как описание, параметры и коды ответов. Это значительно упрощает интеграцию и общение между членами команды и другими командами.

Что добавить при документировании APIS?

  • Описание;
  • Примеры объектов для отправки (POST, PUT и другие)
  • Возможные коды ответа на конечной точке
  • Параметры и их описания.

Чтобы это стало возможным, мы должны добавить этот фрагмент кода в файл .csproj приложения API. Этот фрагмент будет отвечать за включение генерации файла документации с использованием Swagger и отключит некоторые ненужные предупреждения.

<PropertyGroup>
  <generateDocumentationFile>true</generateDocumentationFile>
  <NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>

Теперь мы можем создать метод документации в контроллере:
— Убедитесь, что у вас установлено расширение VSCode: Комментарии к XML-документации C#
— Затем введите 3 косых черты («/») над выбранным методом документирования.

Теперь вы можете поместить некоторую информацию следующим образом:

// api/dev-todo HTTP POST
/// <summary>
/// Add a To Do task.
/// </summary>
/// <remarks>
///  { "title": "Clean Architecture", "Description": "Definition and examples.", "IsDone": true }
/// </remarks>
/// <param name="model">Todo Task data</param>
/// <returns>Object created</returns>
/// <response code="201">Success</response>

Я помещаю json-пример объекта, который этот метод, в случае HttpPost, может обслуживать.

Наконец, в Program.cs мы устанавливаем следующий код:

builder.Services.AddSwaggerGen(c => {
    c.SwaggerDoc("v1", new OpenApiInfo {
        Title = "DevTodoList.API",
        Version = "v1",
        Contact = new OpenApiContact {
            Name = "Mario Alves",
            Email = "[email protected]",
            LinkedIn = new Uri("https://www.linkedin.com/in/marioalvesneto/")
        }
    });

    var xmlFile = "DevTodoList.API.xml";
    var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
    c.IncludeXmlComments(xmlPath);

});

Это может помочь другим разработчикам ответить на вопросы, которые могут у них возникнуть при использовании нашего API.
В дополнение к вставке комментариев в метод с помощью данных, введенных в контроллере.

Чтобы выполнить API, не забудьте просто запустить следующую команду в каталоге проекта:

dotnet run

Логирование с помощью Serilog 🔍

Serilog — это инструмент ведения журналов, предназначенный для мониторинга и облегчения мониторинга и потока журналов из приложения .NET.

  • Serilog — это библиотека, которая позволяет вести журнал для приложений .NET.
  • Он поддерживает множество форматов, таких как SQL Server, SQLite, PostgreSQL и другие.
  • Библиотека для ядра [ASP.NET](http://ASP.NET), которую мы будем использовать, называется Serilog.AspNetCore.
    Мы устанавливаем две Пакеты Nuget:
    Serilog.Sinks.MSSqlServer
    Serilog.AspNetCore

Теперь снова заходим в Program.cs:

// Configuration to Serilog database registration.
builder.Host.ConfigureAppConfiguration((hostingContext, config) => {
    Serilog.Log.Logger = new LoggerConfiguration()
        .Enrich.FromLogContext()
        .WriteTo.MSSqlServer(connectionString,
            sinkOptions: new MSSqlServerSinkOptions() {
                AutoCreateSqlTable = true,
                TableName = "Logs"
            })
        .WriteTo.Console()
        .CreateLogger();
}).UseSerilog();

Здесь мы в основном определяем, что мы хотим автоматически создать таблицу Sql с именем Logs, которая будет содержать журналы ОС реестра, сгенерированные Serilog.
Теперь при тестировании базы данных возвращается ошибка с использованием Serilog на практике:

Теперь при вызове метода GetAll таблица и реестр логов будут сохраняться в SQL Server:

Это очень полезно, потому что показывает журналы, которые мы хотим показать, а также все другие журналы, такие как успехиисключение. Мы можем использовать его, чтобы показать эти журналы в более «красивом» и удобном интерфейсе, например, для пользователя или администратора приложения.

Публикация в облаке ☁️

Теперь мы публикуем приложение в облаке. Я буду использовать службу приложений Azure, где есть бесплатный уровень для тестирования в облаке Azure.

  • Будьте внимательны при загрузке приложения: проверьте версию .NET приложения при загрузке в облако, она должна совпадать с созданной службой приложений Azure. Пример. Приложение находится в .NET 7, поэтому обязательно, чтобы Служба приложений работала с .NET в этой версии.
  • Шаг за шагом для публикации ресурса в облаке:

  • Как только это будет сделано и об этих важных деталях позаботятся, мы сможем загрузить приложение.
  • Во-первых, отключите интеграцию базы данных, поскольку у нас не будет базы данных в облаке (в этом примере), и оставим только базу данных в памяти.
  • Затем мы можем установить расширение VSCode Azure App Service, чтобы легко загрузить приложение в облако Azure.

Установив и войдя в свою учетную запись Azure из расширения VSCode, просто загрузите свое приложение в созданную службу приложений Azure:

Если вы сейчас запустите приложение из Azure и добавите /swagger к URL-адресу, вы обнаружите, что приложение работает нормально.

Бонус: использование API во внешнем интерфейсе📝

В качестве бонуса к этой истории здесь, на Medium, я подготовил свой собственный интерфейс для использования этого API здесь, который мы создали ⭐️ в центре внимания проекта был API, но я создаю этот интерфейс, чтобы показать структуру интерфейса .net Blazor, это потрясающе, и я очень рекомендую.

Важная деталь для использования нашего API во внешнем интерфейсе: знаменитая ошибка CORS. Чтобы этого избежать, нам нужно добавить следующий код в program.cs в проекте API:

builder.Services.AddCors( options => {
   options.AddDefaultPolicy( 
    policy =>
        { 
            policy.WithOrigins("frontend_url");
        });
});

app.UseCors();

Затем добавьте URL-адрес туда в frontend_url и настройте свой интерфейс для получения информации, поступающей от вашего API.
В итоге мой интерфейс выглядел так:

Очень просто, можно многое улучшить, хе-хе.

Ссылка на репозиторий: https://github.com/marioalvesx/DevToDoList

Спасибо, надеюсь, вам понравится!