GeneralNotesAPI - Yago-Captain/GeneralNotes GitHub Wiki

API

Nesta seção, será o diretório da API da aplicação, que engloba todas as classes e funcionalidades relacionadas à exposição de endpoints RESTful e à comunicação entre o cliente e o servidor. A camada da API desempenha um papel central na interação do sistema com o mundo externo, fornecendo interfaces acessíveis e seguras para acesso aos recursos e funcionalidades do aplicativo.

AuthenticatedUserAttribute

using Microsoft.AspNetCore.Mvc;
using MyGeneralNotes.API.Filters;

namespace MyGeneralNotes.API.Attributes;

public class AuthenticatedUserAttribute : TypeFilterAttribute
{
    public AuthenticatedUserAttribute() : base(typeof(AuthenticatedUserFilter))
    {
    }
} 

Esta é uma classe que herda de TypeFilterAttribute, que é uma classe do ASP.NET Core. Ela é usada para aplicar o filtro AuthenticatedUserFilter a uma ação ou controlador. Quando essa classe é usada como um atributo, o filtro AuthenticatedUserFilter é aplicado.


StringConverter

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace MyGeneralNotes.API.Converters;

public partial class StringConverter : JsonConverter<string>
{
    public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
    {
        var value = reader.GetString()?.Trim();

        if (value is null)
            return null;

        return RemoveExtraWhiteSpaces().Replace(value, " "); ;
    }

    public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options) => writer.WriteStringValue(value);

    [GeneratedRegex(@"\s+")]
    private static partial Regex RemoveExtraWhiteSpaces();
}

Esta é uma classe que herda de JsonConverter<string>, que é uma classe do System.Text.Json. Ela define como as strings devem ser deserializadas e serializadas no formato JSON. O método Read é chamado para deserializar uma string do JSON. Ele lê a string do Utf8JsonReader, remove espaços em branco extras e retorna a string. O método Write é chamado para serializar uma string para o JSON. Ele escreve a string no Utf8JsonWriter. O método RemoveExtraWhiteSpaces é um método gerado que retorna uma expressão regular para corresponder a espaços em branco extras.


AuthenticatedUserFilter

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.IdentityModel.Tokens;
using MyGeneralNotes.Communication.Responses;
using MyGeneralNotes.Domain.Extensions;
using MyGeneralNotes.Domain.Repositories.User;
using MyGeneralNotes.Domain.Security.Tokens;
using MyGeneralNotes.Exceptions;
using MyGeneralNotes.Exceptions.ExceptionsBase;
namespace MyGeneralNotes.API.Filters;

public class AuthenticatedUserFilter(IAccessTokenValidator accessTokenValidator, IUserReadOnlyRepository repository) : IAsyncAuthorizationFilter
{
    private readonly IAccessTokenValidator _accessTokenValidator = accessTokenValidator;
    private readonly IUserReadOnlyRepository _repository = repository;

    public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
    {
        try
        {
            var token = TokenOnRequest(context);

            var userIdentifier = _accessTokenValidator.ValidateAndGetUserIdentifier(token);
            var exist = await _repository.ExistActiveUserWitchIdentifier(userIdentifier);

            if (exist.IsFalse())
            {
                throw new MyGeneralNotesExceptions(MessagesException.USER_WITHOUT_PERMISSION);
            }
        }
        catch (SecurityTokenExpiredException)
        {
            context.Result = new UnauthorizedObjectResult(new ResponseError("TokenIsExpired")
            {
                TokenIsExpired = true,
            });
        }
        catch (MyGeneralNotesExceptions ex)
        {
            context.Result = new UnauthorizedObjectResult(new ResponseError(ex.Message));
        }
        catch
        {
            context.Result = new UnauthorizedObjectResult(new ResponseError(MessagesException.USER_WITHOUT_PERMISSION));
        }
    }

    private static string TokenOnRequest(AuthorizationFilterContext context)
    {
        var auth = context.HttpContext.Request.Headers.Authorization.ToString();
        if (string.IsNullOrEmpty(auth))
        {
            throw new MyGeneralNotesExceptions(MessagesException.NO_TOKEN);
        }
        return auth["Bearer ".Length..].Trim();
    }
}

Esta é uma classe que implementa a interface IAsyncAuthorizationFilter do ASP.NET Core. Ela é usada para adicionar lógica de autorização personalizada aos controladores ou ações. Aqui estão os passos principais que ela executa: Obtém o token de autorização do cabeçalho da solicitação. Valida o token e obtém o identificador do usuário usando o IAccessTokenValidator. Verifica se o usuário existe e está ativo usando o IUserReadOnlyRepository. Se o token estiver expirado, se o usuário não existir ou se ocorrer algum outro erro, ela define o resultado da ação para um objeto de resultado não autorizado.


ExceptionFilters

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using MyGeneralNotes.Communication.Responses;
using MyGeneralNotes.Exceptions;
using MyGeneralNotes.Exceptions.ExceptionsBase;
using System.Net;

namespace MyGeneralNotes.API.Filters;

public class ExceptionFilters : IExceptionFilter
{
    public void OnException(ExceptionContext context)
    {
        if (context.Exception is MyGeneralNotesExceptions)
            HandleProjectException(context);
        else
        {
            ThrowUnknowException(context);
        }
    }

    private static void HandleProjectException(ExceptionContext context)
    {
        if (context.Exception is InvalidLoginException)
        {
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.Unauthorized;
            context.Result = new UnauthorizedObjectResult(new ResponseError(context.Exception.Message));
        }
        else if (context.Exception is ErrorOnValidationException)
        {
            var exception = context.Exception as ErrorOnValidationException;
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
            context.Result = new BadRequestObjectResult(new ResponseError(exception!.ErrorsMessages));
        }
    }

    private static void ThrowUnknowException(ExceptionContext context)
    {
        if (context.Exception is ErrorOnValidationException)
        {
            context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
            context.Result = new BadRequestObjectResult(new ResponseError(MessagesException.UNKNOWN_ERROR));
        }
    }
}

Esta é uma classe que implementa a interface IExceptionFilter do ASP.NET Core. Ela é usada para adicionar lógica de tratamento de exceção personalizada aos controladores ou ações. Aqui estão os passos principais que ela executa: Verifica se a exceção é uma MyGeneralNotesExceptions. Se for uma InvalidLoginException ou ErrorOnValidationException, ela define o código de status da resposta e o resultado da ação para um objeto de resultado não autorizado ou de solicitação incorreta, respectivamente. Se a exceção não for uma MyGeneralNotesExceptions, ela define o código de status da resposta e o resultado da ação para um objeto de resultado de solicitação incorreta.


CultureMiddleware

using MyGeneralNotes.Domain.Extensions;
using System.Globalization;

namespace MyGeneralNotes.API.Middleware;

public class CultureMiddleware(RequestDelegate nextRequest)
{
    private readonly RequestDelegate _nextRequest = nextRequest;

    public async Task Invoke(HttpContext context)
    {
        var supportedLeng = CultureInfo.GetCultures(CultureTypes.AllCultures).ToList();

        var requestedCulture = context.Request.Headers.AcceptLanguage.FirstOrDefault();

        var cultureInfo = new CultureInfo("en");

        if (requestedCulture.NotEmpty()
            && supportedLeng.Exists(c => c.Name.Equals(requestedCulture)))
        {
            cultureInfo = new CultureInfo(requestedCulture);
        }

        CultureInfo.CurrentCulture = cultureInfo;
        CultureInfo.CurrentUICulture = cultureInfo;

        await _nextRequest(context);
    }
}

Esta é uma classe de middleware personalizada que configura a cultura atual do thread com base no cabeçalho Accept-Language da solicitação HTTP. Aqui estão os passos principais que ela executa: Obtém a lista de todas as culturas suportadas. Obtém a cultura solicitada do cabeçalho Accept-Language. Se a cultura solicitada estiver vazia ou não for suportada, ela usa a cultura padrão “en” (inglês). Caso contrário, ela usa a cultura solicitada. Define a cultura atual e a cultura da interface do usuário atual para a cultura selecionada. Chama o próximo middleware na cadeia.


HttpContextTokenValue

using MyGeneralNotes.Domain.Security.Tokens;

namespace MyGeneralNotes.API.Token;

public class HttpContextTokenValue : ITokenProvider
{
    private readonly IHttpContextAccessor _contextAccessor;

    public HttpContextTokenValue(IHttpContextAccessor contextAccessor)
    {
        _contextAccessor = contextAccessor;
    }

    public string Value()
    {
        var auth = _contextAccessor.HttpContext!.Request.Headers.Authorization.ToString();

        return auth["Bearer ".Length..].Trim();
    }
}

Esta é uma classe que implementa a interface ITokenProvider. Ela obtém o token de autorização do cabeçalho da solicitação HTTP. Aqui estão os passos principais que ela executa: Obtém o cabeçalho de autorização da solicitação HTTP, remove o prefixo "Bearer " e espaços em branco extras do token, e depois retorna o token.


appsettings.Development.json

O arquivo appsettings.Development.json é usado em projetos ASP.NET Core para armazenar configurações específicas do ambiente de desenvolvimento. Ele contém diferentes seções, cada uma definindo configurações específicas para diferentes partes do aplicativo.

Aqui está uma explicação das principais seções e configurações dentro deste arquivo:

{
  "ConnectionStrings": {
    "ConnectionMySql": "sua-conexão-aqui;"
  },
  "Settings": {
    "Password": {
      "AdditionalKey": "sua-chave-de-criptografia-aqui"
    },
    "Jwt": {
      "SigninKey": "sua-chave-token-aqui",
      "TokenLifeTime": "1000"
    }
  }
}
  • ConnectionStrings: Esta seção contém configurações relacionadas às strings de conexão do banco de dados. No exemplo fornecido, há uma única string de conexão chamada ConnectionMySql, que define os detalhes de conexão para um banco de dados MySQL hospedado localmente.

  • Settings: Esta seção contém configurações personalizadas específicas do aplicativo. No exemplo fornecido, existem configurações para senhas adicionais e JWT (JSON Web Token).

    • Password: Aqui, é definida uma configuração adicional de senha chamada AdditionalKey, que é usada para complementar a segurança das senhas no aplicativo.

    • Jwt: Nesta parte, estão as configurações relacionadas aos JWTs. SigninKey define a chave usada para assinar os tokens JWT. TokenLifeTime define o tempo de vida dos tokens JWT em segundos.

Essas configurações são acessadas pelo aplicativo durante o tempo de execução para personalizar seu comportamento de acordo com as necessidades do ambiente de desenvolvimento.


Program

using Microsoft.OpenApi.Models;
using MyGeneralNotes.API.Converters;
using MyGeneralNotes.API.Filters;
using MyGeneralNotes.API.Middleware;
using MyGeneralNotes.API.Token;
using MyGeneralNotes.Application;
using MyGeneralNotes.Domain.Security.Tokens;
using MyGeneralNotes.Infrastructure;
using MyGeneralNotes.Infrastructure.Extensions;
using MyGeneralNotes.Infrastructure.Migrations;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddCors();

// Add services to the container.
builder.Services.AddControllers().AddJsonOptions(opt => opt.JsonSerializerOptions.Converters.Add(new StringConverter()));
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(opt =>
{
    opt.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = @"JWT Authorization header using Bearer scheme.
                      Enter 'Bearer' [space] and then your token in the text input below.
                      Example: 'Bearer 1123qweqwe'",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        Scheme = "Bearer"
    });
    opt.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference = new OpenApiReference
                {
                    Type = ReferenceType.SecurityScheme,
                    Id = "Bearer"
                },
                Scheme = "oauth2",
                Name = "Bearer",
                In = ParameterLocation.Header
            },
            new List<string>()
        }
    });
});
builder.Services.AddMvc(options => options.Filters.Add(typeof(ExceptionFilters)));

builder.Services.AddApplication();
builder.Services.AddInfrastructure(builder.Configuration);
builder.Services.AddScoped<ITokenProvider, HttpContextTokenValue>();

builder.Services.AddRouting(opt => opt.LowercaseUrls = true);
builder.Services.AddHttpContextAccessor();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseMiddleware<CultureMiddleware>();

app.UseCors(c => c.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod());

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

MigrateDatabase();

app.Run();

void MigrateDatabase()
{
    var connectionString = builder.Configuration.ConnectionString();
    var serviceScope = app.Services.GetRequiredService<IServiceScopeFactory>().CreateScope();
    DatabaseMigration.Migrate(connectionString, serviceScope.ServiceProvider);
}

A classe Program.cs configura e inicia o aplicativo web. Cada parte desse arquivo é explicada em detalhes, incluindo os serviços adicionados, configurações do Swagger, configuração do middleware e migrações de banco de dados. Este é um exemplo de um arquivo Program.cs em um projeto ASP.NET Core. Ele configura e inicia o aplicativo web. Vamos entender o que cada parte faz:

  • builder.Services.AddCors();: Este método habilita o CORS (Cross-Origin Resource Sharing) no aplicativo. O CORS é um mecanismo que permite que muitos recursos (por exemplo, fontes, JavaScript, etc.) em uma página da web sejam solicitados de outro domínio fora do domínio da qual a origem do recurso veio.
  • builder.Services.AddControllers().AddJsonOptions(opt => opt.JsonSerializerOptions.Converters.Add(new StringConverter()));: Este método adiciona os serviços necessários para usar controladores no aplicativo. Ele também configura as opções de serialização JSON para usar um conversor personalizado StringConverter.
  • builder.Services.AddEndpointsApiExplorer();: Este método adiciona os serviços necessários para a exploração da API de endpoints no aplicativo.
  • builder.Services.AddSwaggerGen(opt => {...});: Este método adiciona os serviços do Swagger e configura a geração do Swagger. O Swagger é uma ferramenta que ajuda a construir, documentar e consumir APIs RESTful.
  • builder.Services.AddMvc(options => options.Filters.Add(typeof(ExceptionFilters)));: Este método adiciona os serviços do MVC ao aplicativo e configura um filtro de exceção global.
  • builder.Services.AddApplication();: Este método adiciona os serviços definidos na camada de aplicação ao container de injeção de dependência.
  • builder.Services.AddInfrastructure(builder.Configuration);: Este método adiciona os serviços definidos na camada de infraestrutura ao container de injeção de dependência.
  • builder.Services.AddScoped<ITokenProvider, HttpContextTokenValue>();: Este método registra a implementação HttpContextTokenValue do serviço ITokenProvider com um ciclo de vida Scoped.
  • builder.Services.AddRouting(opt => opt.LowercaseUrls = true);: Este método adiciona os serviços de roteamento ao aplicativo e configura as opções de roteamento para usar URLs em minúsculas.
  • builder.Services.AddHttpContextAccessor();: Este método adiciona um serviço que fornece acesso ao HttpContext atual.
  • app.UseMiddleware<CultureMiddleware>();: Este método adiciona o middleware CultureMiddleware ao pipeline de processamento de solicitações HTTP.
  • app.UseCors(c => c.AllowAnyHeader().AllowAnyOrigin().AllowAnyMethod());: Este método adiciona o middleware CORS ao pipeline de processamento de solicitações HTTP e configura a política CORS para permitir qualquer cabeçalho, origem e método.
  • app.UseHttpsRedirection();: Este método adiciona o middleware de redirecionamento HTTPS ao pipeline de processamento de solicitações HTTP.
  • app.UseAuthorization();: Este método adiciona o middleware de autorização ao pipeline de processamento de solicitações HTTP.
  • app.MapControllers();: Este método mapeia os controladores de API no pipeline de roteamento.
  • MigrateDatabase();: Este método executa as migrações do banco de dados.

Controllers

Os controllers da API são componentes essenciais para o projeto, pois são os responsáveis por lidar com as requisições HTTP e retornar as respostas adequadas solicitadas. Eles organizam e expõem os endpoints da API, definindo a lógica para cada operação suportada.

UserController

Este controller gerencia operações relacionadas aos usuários, como registro, obtenção de perfil, atualização e alteração de senha. Os métodos protegidos pelo atributo [AuthenticatedUser] exigem autenticação para acesso.

[AuthenticatedUser]
public class UserController : MyGeneralNotesController
{
    [HttpPost]
    [ProducesResponseType(typeof(ResponseRegisteredUser), StatusCodes.Status201Created)]
    public async Task<IActionResult> Register(
        [FromServices] IRegisterUserUseCase registerUserUse,
        [FromBody] RequestRegisteredUser request)
    {
        var result = await registerUserUse.Execute(request);
        return Created(string.Empty, result);
    }

    [HttpGet]
    [ProducesResponseType(typeof(ResponseUserProfile), StatusCodes.Status200OK)]
    [AuthenticatedUser]
    public async Task<IActionResult> GetUserProfile([FromServices] IGetUserProfileUseCase userProfile)
    {
        var result = await userProfile.Execute();
        return Ok(result);
    }

    [HttpPut]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(typeof(ResponseError), StatusCodes.Status400BadRequest)]
    [AuthenticatedUser]
    public async Task<IActionResult> Update(
        [FromServices] IUpdateUserUseCase useCase,
        [FromBody] RequestUpdateUser request)
    {
        await useCase.Execute(request);
        return NoContent();
    }

    [HttpPatch]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    [ProducesResponseType(typeof(ResponseError), StatusCodes.Status400BadRequest)]
    [AuthenticatedUser]
    public async Task<IActionResult> ChangePassword(
        [FromServices] IChangePasswordUseCase useCase,
        [FromBody] RequestChangePassword request)
    {
        await useCase.Execute(request);
        return NoContent();
    }
}

RoutineController

Este controller gerencia operações relacionadas às rotinas, como registro, obtenção por ID, atualização e exclusão.

public class RoutineController : MyGeneralNotesController
{
    [HttpPost]
    [ProducesResponseType(typeof(ResponseRoutine), StatusCodes.Status201Created)]
    public async Task<IActionResult> RegisterRoutine(
    [FromServices] IRegisterRoutineUseCase useCase,
    [FromBody] RequestRoutine request)
    {
        var response = await useCase.Execute(request);
        return Created(string.Empty, response);
    }

    [HttpGet("{routineId}")]
    [ProducesResponseType(typeof(ResponseRoutine), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetRoutine(
    [FromServices] IGetRoutineByIdUseCase useCase,
    [FromRoute] long routineId)
    {
        var response = await useCase.GetRoutineById(routineId);
        return Ok(response);
    }

    [HttpPut]
    [Route("{routineId}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> UpdateRoutine(
    [FromServices] IUpdateRoutineUseCase useCase,
    [FromBody] RequestRoutine request,
    [FromRoute] long routineId)
    {
        await useCase.Update(routineId, request);
        return NoContent();
    }

    [HttpDelete]
    [Route("{routineId}")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> DeleteRoutine(
    [FromServices] IDeleteRoutineUseCase useCase,
    [FromRoute] long routineId)
    {
        await useCase.Delete(routineId);
        return NoContent();
    }
}

LoginController

Este controller gerencia operações relacionadas ao login de usuários.

public class LoginController : MyGeneralNotesController
{
    [HttpPost]
    [ProducesResponseType(typeof(ResponseRegisteredUser), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ResponseError), StatusCodes.Status401Unauthorized)]
    public async Task<IActionResult> Login([FromServices] IDoLoginUseCase loginUseCase, [FromBody] RequestLogin request)
    {
        var response = await loginUseCase.Execute(request);
        return Ok(response);
    }
}

DashboardController

Este controller gerencia operações relacionadas ao dashboard da aplicação. O método protegido pelo atributo [AuthenticatedUser] requer autenticação para acesso.

[AuthenticatedUser]
public class DashboardController : MyGeneralNotesController
{
    [HttpPut]
    [ProducesResponseType(typeof(ResponseDashboard), StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> Dashboard(
    [FromServices] IDashboardUseCase dashboard,
    [FromBody] RequestDashboard request)
    {
        var result = await dashboard.GetDashboard(request);
        if (result.Routines.Count != 0)
        {
            return Ok(result);
        }
        return NoContent();
    }
}

MyGeneralNotesController

using Microsoft.AspNetCore.Mvc;

namespace MyGeneralNotes.API.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class MyGeneralNotesController : ControllerBase
    {
    }
}

Esta é uma classe base para todos os controladores na aplicação. Ela herda de ControllerBase, que é uma classe do ASP.NET Core.


Encerramento do Diretório da API

Nesta seção, foi apresentado em detalhes o diretório da API, abrangendo todas as classes e funcionalidades relacionadas à exposição de endpoints RESTful e à comunicação entre o cliente e o servidor. A camada da API desempenha um papel central na interação do sistema com o mundo externo, fornecendo interfaces acessíveis e seguras para acesso aos recursos e funcionalidades do aplicativo.

As classes e funcionalidades discutidas nesta seção são essenciais para o funcionamento adequado da API, garantindo segurança, controle de acesso e manipulação adequada de exceções e solicitações.

⚠️ **GitHub.com Fallback** ⚠️