2023-03-21 17:34:46
Follow these instructions to build a Backend For Frontend API
- Create an ASP.NET Web Solution. Suffix the solution name with
-BackendForFrontend-API . This will be your start-up project. - Create a class library called
*-BackendForFrontend-Data .- Create an interface called
IEntity
.public interface IEntity { string Id { get; set; } }
- Create a folder called
Generic . - Create a folder called
BaseAddress within Generic.
Create the following files within this folder:- Create an interface called
IBaseAddress
.public interface IBaseAddress { Uri BaseAddress { get; set; } }
- Create a class called
BaseAddressConfig
.public class BaseAddressConfig : IBaseAddress { public Uri BaseAddress { get; set; } }
- For the monolith create an address manager:
- Interface:
public interface I
Monolith ManagerBaseAddress: IBaseAddress { } - Class:
public class
Monolith ManagerBaseAddress: ICustomerManagerBaseAddress { public Uri BaseAddress { get; set; } publicMonolith ManagerBaseAddress(string baseAddress) { BaseAddress = new Uri(baseAddress); } }
- Interface:
- For each microservice create an interface base address manager:
- Interface:
public interface I
MicroserviceA ManagerBaseAddress: IBaseAddress { } - Class:
public class
MicroserviceA ManagerBaseAddress: ICustomerManagerBaseAddress { public Uri BaseAddress { get; set; } publicMicroserviceA ManagerBaseAddress(string baseAddress) { BaseAddress = new Uri(baseAddress); } }
- Interface:
- Create an interface called
- Create a folder called
Repository within Generic.
Create the following files within this folder:- create a generic repository:
- Interface:
- Class:
public interface IGenericRepository
where T : IEntity { string ResourcePath { get; set; } Task > GetAllAsync(); Task GetByIdAsync(int id); Task StoreNewAsync(T entity); Task UpdateAsync(T entity); Task DeleteAsync(T entity); } public abstract class GenericRepository
: IGenericRepository where T: IEntity { private static readonly HttpClient _client = new HttpClient(); public string ResourcePath { get; set; } public GenericRepository(IConfiguration configuration, IBaseAddress baseAddress) { _client.BaseAddress = baseAddress.BaseAddress; _client.Timeout = new TimeSpan(0, 0, 30); _client.DefaultRequestHeaders.Accept.Clear(); } public async Task > GetAllAsync() { // Setup HttyRequestMessage var request = new HttpRequestMessage(HttpMethod.Get, $"{ResourcePath}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));// Get HttyResponseMessage var response = await _client.SendAsync(request); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var entities = JsonConvert.DeserializeObject>(content); return entities; } public async Task DeleteAsync(T entity) { // Setup HttyRequestMessage var request = new HttpRequestMessage(HttpMethod.Delete, $"{ResourcePath}/{entity.Id}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));// Get HttyResponseMessage var response = await _client.SendAsync(request); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); return (content.Length==0); } public async TaskGetByIdAsync(int id) { // Setup HttyRequestMessage var request = new HttpRequestMessage(HttpMethod.Get, $"{ResourcePath}/{id}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));// Get HttyResponseMessage var response = await _client.SendAsync(request);// response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var entities = JsonConvert.DeserializeObject(content); return entities; } public async Task StoreNewAsync(T entity) { // Setup HttyRequestMessage var entityToCreate = JsonConvert.SerializeObject(entity); var request = new HttpRequestMessage(HttpMethod.Post, $"{ResourcePath}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = new StringContent(entityToCreate); request.Content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");// Get HttyResponseMessage var response = await _client.SendAsync(request); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var newEntity = JsonConvert.DeserializeObject(content); return (newEntity.Id>0); } public async Task UpdateAsync(T entity) { // Setup HttyRequestMessage var entityToUpdate = JsonConvert.SerializeObject(entity); var request = new HttpRequestMessage(HttpMethod.Put, $"{ResourcePath}/{entity.Id}"); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = new StringContent(entityToUpdate); request.Content.Headers.ContentType = new MediaTypeWithQualityHeaderValue("application/json");// Get HttyResponseMessage var response = await _client.SendAsync(request); response.EnsureSuccessStatusCode(); var content = await response.Content.ReadAsStringAsync(); var newEntity = JsonConvert.DeserializeObject(content); return (newEntity.Id > 0); } } - For the monolith create a repository:
- Interface:
- Class:
public interface I
Monolith Repository: IGenericRepository where T : IEntity { } public class
Monolith Repository: GenericRepository , I Monolith Repositorywhere T : IEntity { public Monolith Repository(IConfiguration configuration, IMonolith BaseAddress baseAddress) : base(configuration, baseAddress) {} } - For each microservice create a repository:
- Interface:
- Class:
public interface I
MicroserviceA ManagerRepository: IGenericRepository where T : IEntity { } public class
MicroserviceA ManagerRepository: GenericRepository , I MicroserviceA ManagerRepositorywhere T : IEntity { public MicroserviceA ManagerRepository(IConfiguration configuration, IMicroserviceA ManagerBaseAddress baseAddress) : base(configuration, baseAddress) {} }
- create a generic repository:
- Create an interface called
- Create a class library called
*-BackendForFrontend-Core . This will hold amodel of your monolith and any enumerators for the model.- Reference
*-BackendForFrontend-Data . - Create a folder called
Domain . - Populate the Domain folder with POCO classes representing the model. Each class should inherit from
*_BackendForFrontend_Data.Generic.IEntity
.
A source code generator can do this automatically and with perfect accuracy.
Example of anInvoice
POCO:using System; using System.Collections.Generic; using *_BackendForFrontend_Data.Generic; namespace *_BackendForFrontend_Core.Domain { public class
Invoice : IEntity { public string Id { get; set; } public string CustomerId { get; set; } public Domain.Customer Customer { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public string Message { get; set; } public ListInvoiceLines { get; set; } } }
- Reference
- Reference
*-BackendForFrontend-Core and*-BackendForFrontend-Data in-BackendForFrontend-API . - Add the following NuGet packages to
-BackendForFrontend-API :- AutoMapper.Extensions.Microsoft.DependencyInjection
- Microsoft.Extensions.Configuration.Binder
- Swashbuckle.AspNetCore
- Edit
appsettings.json
to add the following:"
Monolith BaseAddress": "https://localhost:5000", "MicroserviceA BaseAddress": "https://localhost:5003"The base addresses will be different for each project.
- Create a
Models folder. For each model within the monolith create a POCO class
Example of aInvoice
POCO:using System; namespace *_BackendForFrontend_API.Models { public class
Invoice { public string Id { get; set; } public string CustomerId { get; set; } public DateTime InvoiceDate { get; set; } public DateTime DueDate { get; set; } public string Message { get; set; } } } - Create a folder called
AutoMapper .- Within this folder create a class called
MapperProfiles
. It should inherit fromAutoMapper.Profile
.using AutoMapper; using Core = AM_BackendForFrontend_Core; namespace AM_BackendForFrontend_API.AutoMapper { public class MapperProfiles : Profile { public MapperProfiles() {
// Repeat the following code for each POCO in the monolith class: CreateMap<Models. CreateMap<Models.POCO , Core.Domain.POCO >() .ForMember(c => c.Id, opt => opt.Ignore()) .ReverseMap(); For Example:Invoice , Core.Domain.Invoice >() .ForMember(c => c.Id, opt => opt.Ignore()) .ReverseMap(); } } }
- Within this folder create a class called
- Create a controller for each POCO in the monolith.
Example of aInvoice
POCO:using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.Logging; using System; using System.Threading.Tasks; using *_BackendForFrontend_API.Models; using *_BackendForFrontend_Data.Generic; using AutoMapper; using Core = *_BackendForFrontend_Core; namespace *_BackendForFrontend_API.Controllers { [Route("api/[controller]")] [ApiController] public class
Invoice Controller : ControllerBase { private readonly IAccountManagerRepository_repository; private readonly IMapper _mapper; private readonly LinkGenerator _linkGenerator; private readonly ILogger _logger; public InvoiceController( IAccountManagerRepository repository, IMapper mapper, LinkGenerator linkGenerator, ILogger logger) { _repository = repository; _repository.ResourcePath = "api/Invoice"; _mapper = mapper; _linkGenerator = linkGenerator; _logger = logger; } [HttpGet] public async Task > Get() { try { var results = await _repository.GetAllAsync(); return _mapper.Map (results); } catch (Exception e) { _logger.LogError(e, e.Message); return StatusCode( StatusCodes.Status500InternalServerError, "Failed Request"); } } [HttpGet("{id}")] public async Task > Get(string id) { try { var result = await _repository.GetByIdAsync(id); if (result == null) return NotFound(); return _mapper.Map (result); } catch (Exception e) { _logger.LogError(e, e.Message); return StatusCode( StatusCodes.Status500InternalServerError, "Failed Request"); } } [HttpPost] public async Task > Post(Invoice model) { try { // Make sure InvoiceId is not already taken var existing = await _repository.GetByIdAsync(model.Id); if (existing != null) return BadRequest("Invoice Id in Use");// map var Invoice = _mapper.Map(model); //save and return if (!await _repository.StoreNewAsync(Invoice)) return BadRequest("Bad request, could not create record!"); var location = _linkGenerator.GetPathByAction( "Get", "Invoice", new { Id = Invoice.Id }); return Created(location, _mapper.Map (Invoice)); } catch (Exception e) { _logger.LogError(e, e.Message); return StatusCode( StatusCodes.Status500InternalServerError, "Failed Request"); } } [HttpPut("{id}")] public async Task > Put( string id, Invoice updatedModel) { try { var currentInvoice = await _repository.GetByIdAsync(id); if (currentInvoice == null) return NotFound($"Could not find Invoice with Id of {id}"); _mapper.Map(updatedModel, currentInvoice); if (await _repository.UpdateAsync(currentInvoice)) return _mapper.Map (currentInvoice); } catch (Exception e) { _logger.LogError(e, e.Message); return StatusCode( StatusCodes.Status500InternalServerError, "Failed Request"); } return BadRequest(); } [HttpDelete("{id}")] public async Task Delete(string id) { try { var Invoice = await _repository.GetByIdAsync(id); if (Invoice == null) return NotFound(); if (await _repository.DeleteAsync(Invoice)) return Ok(); } catch (Exception e) { _logger.LogError(e, e.Message); return StatusCode( StatusCodes.Status500InternalServerError, "Failed Request"); } return BadRequest("Failed to delete the Invoice"); } } } - Edit
Startup.cs by adding the following toConfigure :services.AddControllers(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddScoped<IBaseAddress, BaseAddressConfig>();
// For each micro service services.AddSingleton<IMicroServiceA BaseAddress>(a => new CustomerManagerBaseAddress(Configuration.GetValue<string>("MicroServiceA BaseAddress"))); services.AddSingleton(typeof(IMicroServiceA Repository<>), typeof(MicroServiceA Repository<>)); services.AddSingleton<IMonolith BaseAddress>(a => newMonolith BaseAddress(Configuration.GetValue(" Monolith BaseAddress"))); services.AddSingleton(typeof(IMonolith Repository<>), typeof(Monolith Repository<>)); services.RegisterSwagger(); - Edit
Startup.cs by adding the following toConfigure :if (env.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseHttpsRedirection(); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); });