#Developing the Web API Business Rules

#START FROM PREVIOUS MODULE'S END

Developing the API endpoints

#ADD CROSS ORIGIN RESOURCE SHARING (CORS) TO API PROJECT

#ADD ADDCORS() TO SERVICESCONFIGURATION.CS

public static void AddCORS(this IServiceCollection services) { services.AddCors(options => { options.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin() .AllowAnyMethod() .AllowAnyHeader()); }); }

UPDATE STARTUP.CS

using Chinook.API.Configurations; var builder = WebApplication.CreateBuilder(args); // Add services to the container. builder.Services.AddConnectionProvider(builder.Configuration); builder.Services.AddAppSettings(builder.Configuration); builder.Services.ConfigureRepositories(); builder.Services.ConfigureSupervisor(); builder.Services.ConfigureValidators(); builder.Services.AddAPILogging(); builder.Services.AddCORS(); var app = builder.Build(); app.UseHttpLogging(); app.UseHttpsRedirection(); app.UseCors(); app.MapControllers(); app.Run();

#ADD CORS TO CONTROLLERS

[Route("api/[controller]")] [ApiController] [EnableCors("CorsPolicy")] public class AlbumController : ControllerBase {

#ADD FLUENTVALIDATION NUGET TO API AND DOMAIN PROJECTS

dos
dotnet add package FluentValidation dotnet add package FluentValidation.AspNetCore

#ADD VALIDATORS TO DOMAIN PROJECT

using Chinook.Domain.ApiModels; using FluentValidation; namespace Chinook.Domain.Validation; public class AlbumValidator : AbstractValidator<AlbumApiModel> { public AlbumValidator() { RuleFor(a => a.Title).NotNull(); RuleFor(a => a.Title).MinimumLength(3); RuleFor(a => a.Title).MaximumLength(160); RuleFor(a => a.ArtistId).NotNull(); } }

#ADD VALIDATORS TO DEPENDENCY INJECTION IN STARTUP IN API PROJECT

public static void ConfigureValidators(this IServiceCollection services) { services.AddFluentValidationAutoValidation() .AddTransient<IValidator<AlbumApiModel>, AlbumValidator>() .AddTransient<IValidator<ArtistApiModel>, ArtistValidator>() .AddTransient<IValidator<CustomerApiModel>, CustomerValidator>() .AddTransient<IValidator<EmployeeApiModel>, EmployeeValidator>() .AddTransient<IValidator<GenreApiModel>, GenreValidator>() .AddTransient<IValidator<InvoiceApiModel>, InvoiceValidator>() .AddTransient<IValidator<InvoiceLineApiModel>, InvoiceLineValidator>() .AddTransient<IValidator<MediaTypeApiModel>, MediaTypeValidator>() .AddTransient<IValidator<PlaylistApiModel>, PlaylistValidator>() .AddTransient<IValidator<TrackApiModel>, TrackValidator>(); }

#ADD CONFIGUREVALIDATORS TO CONFIGURESERVICES

builder.Services.ConfigureValidators();

#GET VALIDATORS FROM DI IN SUPERVISOR

public partial class ChinookSupervisor : IChinookSupervisor { private readonly IAlbumRepository _albumRepository; private readonly IArtistRepository _artistRepository; private readonly ICustomerRepository _customerRepository; private readonly IEmployeeRepository _employeeRepository; private readonly IGenreRepository _genreRepository; private readonly IInvoiceLineRepository _invoiceLineRepository; private readonly IInvoiceRepository _invoiceRepository; private readonly IMediaTypeRepository _mediaTypeRepository; private readonly IPlaylistRepository _playlistRepository; private readonly ITrackRepository _trackRepository; private readonly IValidator<AlbumApiModel> _albumValidator; private readonly IValidator<ArtistApiModel> _artistValidator; private readonly IValidator<CustomerApiModel> _customerValidator; private readonly IValidator<EmployeeApiModel> _employeeValidator; private readonly IValidator<GenreApiModel> _genreValidator; private readonly IValidator<InvoiceApiModel> _invoiceValidator; private readonly IValidator<InvoiceLineApiModel> _invoiceLineValidator; private readonly IValidator<MediaTypeApiModel> _mediaTypeValidator; private readonly IValidator<PlaylistApiModel> _playlistValidator; private readonly IValidator<TrackApiModel> _trackValidator; public ChinookSupervisor(IAlbumRepository albumRepository, IArtistRepository artistRepository, ICustomerRepository customerRepository, IEmployeeRepository employeeRepository, IGenreRepository genreRepository, IInvoiceLineRepository invoiceLineRepository, IInvoiceRepository invoiceRepository, IMediaTypeRepository mediaTypeRepository, IPlaylistRepository playlistRepository, ITrackRepository trackRepository, IValidator<AlbumApiModel> albumValidator, IValidator<ArtistApiModel> artistValidator, IValidator<CustomerApiModel> customerValidator, IValidator<EmployeeApiModel> employeeValidator, IValidator<GenreApiModel> genreValidator, IValidator<InvoiceApiModel> invoiceValidator, IValidator<InvoiceLineApiModel> invoiceLineValidator, IValidator<MediaTypeApiModel> mediaTypeValidator, IValidator<PlaylistApiModel> playlistValidator, IValidator<TrackApiModel> trackValidator ) { _albumRepository = albumRepository; _artistRepository = artistRepository; _customerRepository = customerRepository; _employeeRepository = employeeRepository; _genreRepository = genreRepository; _invoiceLineRepository = invoiceLineRepository; _invoiceRepository = invoiceRepository; _mediaTypeRepository = mediaTypeRepository; _playlistRepository = playlistRepository; _trackRepository = trackRepository; _albumValidator = albumValidator; _artistValidator = artistValidator; _customerValidator = customerValidator; _employeeValidator = employeeValidator; _genreValidator = genreValidator; _invoiceValidator = invoiceValidator; _invoiceLineValidator = invoiceLineValidator; _mediaTypeValidator = mediaTypeValidator; _playlistValidator = playlistValidator; _trackValidator = trackValidator; }

#ADD CODE TO CALL VALIDATION IN SUPERVISOR

public async Task<AlbumApiModel> AddAlbum(AlbumApiModel newAlbumApiModel) { await _albumValidator.ValidateAndThrowAsync(newAlbumApiModel); var album = newAlbumApiModel.Convert(); album = await _albumRepository.Add(album); newAlbumApiModel.Id = album.Id; return newAlbumApiModel; } public async Task<bool> UpdateAlbum(AlbumApiModel albumApiModel) { await _albumValidator.ValidateAndThrowAsync(albumApiModel); var album = await _albumRepository.GetById(albumApiModel.Id); if (album is null) return false; album.Id = albumApiModel.Id; album.Title = albumApiModel.Title; album.ArtistId = albumApiModel.ArtistId; return await _albumRepository.Update(album); }

#ADD ERROR HANDLING IN ACTIONS

[Route("api/[controller]")] [ApiController] [EnableCors("CorsPolicy")] public class AlbumController : ControllerBase { private readonly IChinookSupervisor _chinookSupervisor; private readonly ILogger<AlbumController> _logger; public AlbumController(IChinookSupervisor chinookSupervisor, ILogger<AlbumController> logger) { _chinookSupervisor = chinookSupervisor; _logger = logger; } [HttpGet] [Produces(typeof(List<AlbumApiModel>))] public async Task<ActionResult<List<AlbumApiModel>>> Get() { try { var albums = await _chinookSupervisor.GetAllAlbum(); if (albums.Any()) { return Ok(albums); } else { return StatusCode((int)HttpStatusCode.NotFound, "No Albums Could Be Found"); } } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController Get action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get All Albums"); } } [HttpGet("{id}", Name = "GetAlbumById")] public async Task<ActionResult<AlbumApiModel>> Get(int id) { try { var album = await _chinookSupervisor.GetAlbumById(id); if (album != null) { return Ok(album); } else { return StatusCode((int)HttpStatusCode.NotFound, "Album Not Found"); } } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController GetById action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get Album By Id"); } } [HttpPost] [Produces("application/json")] [Consumes("application/json")] public async Task<ActionResult<AlbumApiModel>> Post([FromBody] AlbumApiModel input) { try { if (input == null) { return StatusCode((int)HttpStatusCode.BadRequest, "Given Album is null"); } else { return Ok(await _chinookSupervisor.AddAlbum(input)); } } catch (ValidationException ex) { _logger.LogError($"The Album could not validated: Add Album action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Validation error while executing Add Albums"); } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController Add Album action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Add Albums"); } } [HttpPut("{id}")] [Produces("application/json")] [Consumes("application/json")] public async Task<ActionResult<AlbumApiModel>> Put(int id, [FromBody] AlbumApiModel input) { try { if (input == null) { return StatusCode((int)HttpStatusCode.BadRequest, "Given Album is null"); } else { return Ok(await _chinookSupervisor.UpdateAlbum(input)); } } catch (ValidationException ex) { _logger.LogError($"The Album could not validated: Update Album action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Validation error while executing Update Albums"); } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController Update Album action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Update Albums"); } } [HttpDelete("{id}")] public async Task<ActionResult> Delete(int id) { try { return Ok(await _chinookSupervisor.DeleteAlbum(id)); } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController Delete action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Delete Album"); } } [HttpGet("artist/{id}")] public async Task<ActionResult<List<AlbumApiModel>>> GetByArtistId(int id) { try { var albums = await _chinookSupervisor.GetAlbumByArtistId(id); if (albums.Any()) { return Ok(albums); } else { return StatusCode((int)HttpStatusCode.NotFound, "No Albums Could Be Found for the Artist"); } } catch (Exception ex) { _logger.LogError($"Something went wrong inside the AlbumController Get By Artist action: {ex}"); return StatusCode((int)HttpStatusCode.InternalServerError, "Error occurred while executing Get All Albums for Artist"); } } }

#ERROR HANDLING AND RETURNING PROBLEM DETAILS

For more details look at the Chinook6WebAPI repo in GitHub.