diff --git a/.NET/ASP.NET/REST API.md b/.NET/ASP.NET/REST API.md index 47d0c12..a96e6f5 100644 --- a/.NET/ASP.NET/REST API.md +++ b/.NET/ASP.NET/REST API.md @@ -1,181 +1,5 @@ # ASP .NET REST API -## Startup class - -- Called by `Program.cs` - -```cs -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace -{ - public class Startup - { - public Startup(IConfiguration configuration) - { - Configuration = configuration; - } - - public IConfiguration Configuration { get; } - - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddControllers(); // controllers w/o views - //or - sevices.AddControllersWithViews(); // MVC Controllers - } - - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IWebHostEnvironment env) - { - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - - app.UseHttpsRedirection(); - - app.UseRouting(); - - app.UseAuthorization(); - - app.UseEndpoints(endpoints => - { - endpoints.MapControllers(); - }); - } - } -} -``` - -## DB Context (EF to access DB) - -NuGet Packages to install: - -- `Microsoft.EntityFrameworkCore` -- `Microsoft.EntityFrameworkCore.Tools` -- `Microsoft.EntityFrameworkCore.Design` *or* `Microsoft.EntityFrameworkCore..Design` -- `Microsoft.EntityFrameworkCore.` - -In `AppDbContext.cs`: - -```cs -using .Model; -using Microsoft.EntityFrameworkCore; - -namespace .Repo -{ - public class AppDbContext : DbContext - { - public AppDbContext(DbContextOptions options) : base(options) - { - - } - //DBSet represents the collection of all entities in the context, or that can be queried from the database, of a given type - public DbSet entities { get; set; } - } -} -``` - -In `appsettings.json`: - -```json -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft": "Warning", - "Microsoft.Hosting.Lifetime": "Information" - } - }, - "AllowedHosts": "*", - "ConnectionStrings": { - "CommanderConnection" : "Server=;Database=;UID=;Pwd=" - } -} -``` - -In `Startup.cs`: - -```cs -// This method gets called by the runtime. Use this method to add services to the container. -public void ConfigureServices(IServiceCollection services) -{ - // SqlServer is the db used in this example - services.AddDbContext(option => option.UseSqlServer(Configuration.GetConnectionString("CommanderConnection"))); - services.AddControllers(); -} -``` - -### Migrations - -- Mirroring of model in the DB. -- Will create & update DB Schema if necessary - -In Packge Manager Shell: - -```ps1 -add-migrations -update-database # use the migrations to modify the db -``` - -## Repository - -In `IEntityRepo`: - -```cs -using .Model; -using System.Collections.Generic; - -namespace .Repository -{ - public interface IEntityRepo - { - IEnumerable SelectAll(); - Entity SelectOneById(int id); - - ... - } -} -``` - -In `EntityRepo`: - -```cs -using .Model; -using System.Collections.Generic; - -namespace .Repo -{ - public class EntityRepo : IEntityRepo - { - private readonly AppDbContext _context; - - public EntityRepo(AppDbContext context) - { - _context = context; - } - - public IEnumerable SelectAll() - { - return _context.Entities.ToList(); // linq query (ToList()) becomes sql query - } - - public Entity SelectOneById(int id) - { - return _context.Entities.FirstOrDefault(p => p.Id == id); - } - - ... - } -} -``` - ## Data Transfer Objects (DTOs) A **DTO** is an object that defines how the data will be sent and recieved over the network (usually as JSON). @@ -183,38 +7,28 @@ Without a DTO the JSON response (or reqest) could contain irrilevant, wrong or u Moreover, by decoupling the JSON response from the actual data model, it's possible to change the latter without breaking the API. DTOs must be mapped to the internal methods. -Required NuGet Packages: - -- AutoMapper.Extensions.Microsoft.DependencyInjection - -In `StartUp.cs`: - -```cs -using AutoMapper; - -// ... - -public void ConfigureServices(IServiceCollection services) -{ - // other services - - services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); // set automapper service -} -``` - -In `EntityDTO.cs`: +In `EntityDTO.cs`: ```cs namespace .DTOs { - // define the data to be serialized in JSON (can differ from model) - public class EntityCrudOpDTO // e.g: EntityReadDTO, ... + // define the data to be serialized in JSON (differs from model) + public class EntityDTO { // only properties to be serialized } } ``` +### DTO mapping with Automapper + +Required NuGet Packages: + +- `AutoMapper` +- `AutoMapper.Extensions.Microsoft.DependencyInjection` + +A good way to organize mapping configurations is with *profiles*. + In `EntitiesProfile.cs`: ```cs @@ -228,34 +42,31 @@ namespace .Profiles { public EntitiesProfile() { - CreateMap(); // map entity to it's DTO + CreateMap(); // map entity to it's DTO } } } ``` -## Controller (No View) - -Uses [Dependency Injection](https://en.wikipedia.org/wiki/Dependency_injection) to recieve a suitable implementation of `IEntityRepo`, - -### Service Lifetimes - -- `AddSignleton`: same for every request -- `AddScoped`: created once per client -- `Transient`: new instance created every time - -In `Startup.cs`: +In `StartUp.cs`: ```cs -// This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { - services.AddControllers(); - services.AddScoped(); // map the interface to its implementation, needed for dependency injection + // other services + + // let AutoMapper know in what assemblies are the profiles defined + services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); + + // or create a MapperConfiguration + services.AddAutoMapper(cfg => { + cfg.CreateMap(); + cfg.AddProfile(); + }) } ``` -### Request Mappings +### Controller with DTOs ```cs using .Model; @@ -269,68 +80,35 @@ namespace .Controllers [ApiController] public class EntitiesController : ControllerBase // MVC controller w/o view { - private readonly ICommandRepo _repo; + // service or repo (DAL) injection + private readonly IMapper _mapper; // AutoMapper class - public EntitiesController(IEntityRepo repository, IMapper mapper) // injection og the dependency - { - _repo = repository; - _mapper = mapper - } - [HttpGet] // GET api/endpoint - public ActionResult> SelectAllEntities() + public ActionResult SelectAllEntities() { - var results = _repo.SelectAll(); + ... - return Ok(_mapper.Map(results)); // return an action result OK (200) with the results - } - - // default binding source: [FromRoute] - [HttpGet("{id}")] // GET api/endpoint/{id} - public ActionResult SelectOneEntityById(int id) - { - var result = _repo.SelectOneById(id); - - if(result != null) - { - return Ok(_mapper.Map(result)); // transfrom entity to it's DTO - } - - return NotFound(); // ActionResult NOT FOUND (404) + return Ok(_mapper.Map(entity)); } } } ``` -## Controller (With View) +## Simple API Controller ```cs -using .Model; -using Microsoft.AspNetCore.Mvc; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace .Controllers +[Route("api/endpoint")] +[ApiController] +public class EntitiesController : ControllerBase { - [Route("api/endpoint")] - [ApiController] - public class EntitiesController : Controller + // service or repo (DAL) injection + + [HttpGet] + public ActionResult SelectAll() { - private readonly AppDbContext _db; - - public EntitiesController(AppDbContext db) - { - _db = db; - } - - [HttpGet] - public IActionResult SelectAll() - { - return Json(new { data = _db.Entities.ToList() }); // json view - } + ... + return Ok(entity); } } ```