Open Source @ UNA

Repositorio Open Source de documentación en nuevas tecnologías


Project maintained by tfg2021-escinf-una Hosted on GitHub Pages — Theme by mattgraham

Regresar

Setup

En el siguiente link encontrará el repositorio creado para este ejemplo práctico: https://github.com/tfg2021-escinf-una/eg.identity.management . En caso de tener alguna duda con el código, puede revisar directamente en el repositorio, o bien, clonarlo y modificarlo.

  1. Abrir el repositorio microservices-tutorial/identity-management-microservice en el editor de texto

  2. En este momento la lista de archivos en el proyecto es la siguiente:

microservices-tutorial
│
└─── identity-management-microservice
│   │   README.md
│   │   .gitignore
│   │   LICENSE
  1. Para inicializar el proyecto de C# recomendamos utilizar Visual Studio e utilizar el template que la aplicacion ofrece para la construccion del archivo.

    Por lo general, los proyectos basados en C#, generan un archivo con la extension .csproj, en esta es donde se instalan las dependencias necesarias para su adecuado funcionamiento.
    Un ejemplo de un archivo .csproj es el siguiente: .csproj

  2. Una vez que Visual Studio a creado la estructura de carpetas, vamos a proceder a efectuar un breve cambio en dicha estructura, cabe resaltar, que esta es meramente organizacional y opcional.

microservices-tutorial
│
└─── identity-management-microservice
│   └─── src
│   │   │   ProjectName.csproj
│   │   │   Repositories
│   │   │   Services
│   │   │   Controllers
│   │   │   Program.cs
│   │   deployment
│   │   test
│   │   README.md
│   │   .gitignore
│   │   LICENSE

Análisis de la estructura propuesta.

Análisis de la estructura dentro de src.

src
└─── identity-management-microservice
│   │   ProjectName.csproj
│   │   Repositories
│   │   Services
│   │   Controllers
│   │   Program.cs

  1. Implementación de piezas fundamentales en un API no minimalista.

Ejemplo de un controlador

using AutoMapper;
using EG.IdentityManagement.Microservice.Entities.ViewModels;
using EG.IdentityManagement.Microservice.Identity;
using EG.IdentityManagement.Microservice.Services.Interfaces;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace EG.IdentityManagement.Microservice.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        private readonly IIdentityService _identityService;
        private readonly IMapper _mapper;

        public UserController(IIdentityService identityService,
                              IMapper mapper)
        {
            _identityService = identityService;
            _mapper = mapper;
        }

        [HttpPost]
        [Route("login")]
        public async Task<IActionResult> Login([FromBody] CredentialsViewModel credentials)
            => await _identityService.Login(credentials.EmailAddress, credentials.Password);

        [HttpPost]
        [Route("register")]
        public async Task<IActionResult> Register([FromBody] IdentityViewModel user)
            => await _identityService.Register(_mapper.Map<User>(user));

        [HttpPost]
        [Route("refresh")]
        public async Task<IActionResult> Refresh([FromBody] TokensViewModel token)
            => await _identityService.Refresh(token.jwtToken, token.refreshToken);
    }
}  

Ejemplo de un servicio

using EG.IdentityManagement.Microservice.Customizations.Identity;
using EG.IdentityManagement.Microservice.Entities;
using EG.IdentityManagement.Microservice.Entities.Const;
using EG.IdentityManagement.Microservice.Identity;
using EG.IdentityManagement.Microservice.Services.Interfaces;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;

namespace EG.IdentityManagement.Microservice.Services.Implementations
{
    public class IdentityService : IIdentityService
    {
        private readonly SignInManager<User> _signInManager;
        private readonly CustomUserManager<User> _userManager;

        public IdentityService(SignInManager<User> signInManager, CustomUserManager<User> userManager)
        {
            _signInManager = signInManager ?? throw new ArgumentNullException("SignInManager property should not come as null");
            _userManager = userManager ?? throw new ArgumentNullException("UserManager property should not come as null");
        }

        public async Task<IActionResult> Register(User user)
        {
            dynamic data = new ExpandoObject();
            var errors = new List<List<IdentityError>>();
            var registerResult = await _userManager.CreateAsync(user, user.Password);

            if (registerResult.Succeeded)
            {
                data.IdAssigned = user.Id;
                var roleResult = await _userManager.AddToRoleAsync(user, Constants.ApplicationRoles.StandardUser.ToString());

                if (roleResult.Succeeded)
                    data.RoleAssigned = Constants.ApplicationRoles.StandardUser.ToString();
                else
                    errors.Add(roleResult.Errors.ToList());
            }
            else
            {
                errors.Add(registerResult.Errors.ToList());
            }

            if (errors.Count != 0)
            {
                return new BadRequestObjectResult(new GenericResponse
                {
                    Errors = errors,
                    Data = data,
                    StatusCode = HttpStatusCode.BadRequest
                });
            }

            return new CreatedResult("/", new GenericResponse
            {
                Errors = errors,
                Data = data,
                StatusCode = HttpStatusCode.Created
            });
        }
    }
}

Ejemplo de un repositorio

using EG.IdentityManagement.Microservice.Settings;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using MongoDB.Driver;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace EG.IdentityManagement.Microservice.Repositories
{
    [ExcludeFromCodeCoverage]
    public class MongoRepository<T> : IMongoRepository<T>
    {
        private readonly MongoDbSettings _mongoDbSettings;
        private readonly IMongoClient _mongoClient;
        private readonly ILogger<MongoRepository<T>> _logger;
        private readonly string _collectionName;

        public MongoRepository(IOptions<MongoDbSettings> mongoDbSettings,
                               ILogger<MongoRepository<T>> logger,
                               IMongoClient mongoClient)
        {
            _mongoDbSettings = mongoDbSettings.Value ?? throw new ArgumentNullException("Value should not come as null");
            _mongoClient = mongoClient ?? throw new ArgumentNullException("Value should not come as null");
            _logger = logger;
            _collectionName = $"{typeof(T).Name}s";
        }

        public async Task<bool> InsertOneAsync(T obj)
        {
            try
            {
                var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
                var collection = database.GetCollection<T>(_collectionName);
                await collection.InsertOneAsync(obj);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"An unhandled error has occurred while inserting a document in the collection: {_collectionName}");
                return false;
            }

            return true;
        }

        public async Task<DeleteResult> DeleteOneAsync(FilterDefinition<T> filter, CancellationToken cancellationToken = default)
        {
            var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
            var collection = database.GetCollection<T>(_collectionName);
            return await collection.DeleteOneAsync(filter, cancellationToken);
        }

        public T Find(FilterDefinition<T> filter, FindOptions options = null)
        {
            var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
            var collection = database.GetCollection<T>(_collectionName);
            return collection.Find(filter, options)
                    .FirstOrDefault();
        }

        public async Task<T> FindOneAndUpdateAsync(FilterDefinition<T> filter, UpdateDefinition<T> update, CancellationToken cancellationToken = default)
        {
            var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
            var collection = database.GetCollection<T>(_collectionName);
            return await collection.FindOneAndUpdateAsync(
                filter,
                update,
                options: new FindOneAndUpdateOptions<T>
                {
                    // Do this to get the record AFTER the updates are applied
                    ReturnDocument = ReturnDocument.After
                });
        }

        public async Task<TNewResult> FindOneLookupAsync<TResult, TForeignDocument, TNewResult>(
            string foreignCollection,
            FieldDefinition<T> localField,
            FieldDefinition<TForeignDocument> foreignField,
            FieldDefinition<TNewResult> @as,
            FilterDefinition<T> filter)
        {
            var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
            var collection = database.GetCollection<T>(_collectionName);
            return await collection
                       .Aggregate()
                       .Match(filter)
                       .Lookup(foreignCollection,
                              localField,
                              foreignField,
                              @as)
                      .FirstOrDefaultAsync();
        }

        public async Task<IEnumerable<T>> Find(Expression<Func<T, bool>> filter, FindOptions options = null)
        {
            var database = _mongoClient.GetDatabase(_mongoDbSettings.DatabaseName);
            var collection = database.GetCollection<T>(_collectionName);
            return await collection.Find(filter, options)
                    .ToListAsync();
        }
    }
}
  1. Una vez calzadas todas estas piezas, es posible compilar la solucion sin errores.
  2. Para definir las variables de ambiente es necesario utilizar el archivo denominado appsettings.json
    • Aqui deben ir todas la variables relacionadas a las conexiones de la base de datos.
{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*"
}
  1. Ejecutar el servidor

Una vez realizados todos estos pasos, el servidor está listo para recibir peticiones HTTP en el puerto por definir.

AnteriorSiguiente