《Asp.Net Core3 + Vue3入坑教程》 此教程適合新手入門或者先後端分離嘗試者。能夠根據圖文一步一步進操做編碼也能夠選擇直接查看源碼。每一篇文章都有對應的源碼html
教程後期會將 .Net Core 3升級成 .Net Core 5前端
Asp.Net Core後端項目vue
Vue3 前端項目git
暫未發表敬請期待...github
本文爲《Asp.Net Core3 + Vue3入坑教程》系列教程的後端第三篇 - AutoMapper & Restful API & DI。本文將利用AutoMapper與依賴注入等內容實現一個簡單的Restful API。web
當前項目使用的SKD是 .net core 3後續將SDK升級以後再升級此Nuget包的版本sql
代碼以下:數據庫
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json.Serialization; using Simple_Asp.Net_Core.Data; using Simple_Asp.Net_Core.ServiceProvider; using System; namespace Simple_Asp.Net_Core { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddCORS(); services.AddMvc(); services.AddSwagger(); services.AddControllers().AddNewtonsoftJson(s => { s.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); }); } // 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.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); }); } app.UseCors("CorsTest"); app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); } } }
代碼以下:json
using System.ComponentModel.DataAnnotations; namespace Simple_Asp.Net_Core.Models { public class Command { [Key] [Required] public int Id { get; set; } [Required] [MaxLength(250)] public string HowTo { get; set; } [Required] public string Line { get; set; } [Required] public string Platform { get; set; } } }
using Simple_Asp.Net_Core.Models; using System.Collections.Generic; namespace Simple_Asp.Net_Core.Data { public interface ICommanderRepo { IEnumerable<Command> GetAllCommands(); } }
using Simple_Asp.Net_Core.Models; using System.Collections.Generic; namespace Simple_Asp.Net_Core.Data { public class MockCommanderRepo : ICommanderRepo { public IEnumerable<Command> GetAllCommands() { var commands = new List<Command> { new Command{Id=0, HowTo="Boil an egg", Line="Boil water", Platform="Kettle & Pan"}, new Command{Id=1, HowTo="Cut bread", Line="Get a knife", Platform="knife & chopping board"}, new Command{Id=2, HowTo="Make cup of tea", Line="Place teabag in cup", Platform="Kettle & cup"} }; return commands; } } }
上一步模擬實現了從數據庫返回Command實體,可是在返回給前端的時候不能直接返回實體,而是須要轉換成Dto。根據不一樣的業務場景須要創建不一樣的Dto後端
namespace Simple_Asp.Net_Core.Dtos { public class CommandReadDto { public int Id { get; set; } public string HowTo { get; set; } public string Line { get; set; } } }
生命週期是依賴注入裏很是重要的內容,具體能夠參照官方的文檔
https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes
這裏使用的註冊方式是AddScoped,讓每一次請求都會建立一個實例
For web applications, a scoped lifetime indicates that services are created once per client request (connection). Register scoped services with AddScoped.
Startup.cs代碼調整成以下:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json.Serialization; using Simple_Asp.Net_Core.Data; using Simple_Asp.Net_Core.ServiceProvider; using System; namespace Simple_Asp.Net_Core { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddCORS(); services.AddMvc(); services.AddSwagger(); services.AddScoped<ICommanderRepo, MockCommanderRepo>(); services.AddControllers().AddNewtonsoftJson(s => { s.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); }); } // 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.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); }); } app.UseCors("CorsTest"); app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); } } }
前面已經建立了Command實體與CommandReadDto Dto,如今咱們要讓這Commond實體可以自動轉換成CommandReadDto Dto
代碼以下:
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Newtonsoft.Json.Serialization; using Simple_Asp.Net_Core.Data; using Simple_Asp.Net_Core.ServiceProvider; using System; namespace Simple_Asp.Net_Core { public class Startup { // This method gets called by the runtime. Use this method to add services to the container. // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { services.AddCORS(); services.AddMvc(); services.AddSwagger(); services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); services.AddScoped<ICommanderRepo, MockCommanderRepo>(); services.AddControllers().AddNewtonsoftJson(s => { s.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); }); } // 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.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "ApiHelp V1"); }); } app.UseCors("CorsTest"); app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapDefaultControllerRoute()); } } }
代碼以下:
using AutoMapper; using Simple_Asp.Net_Core.Dtos; using Simple_Asp.Net_Core.Models; namespace Simple_Asp.Net_Core.Profiles { public class CommandsProfile : Profile { public CommandsProfile() { //Source -> Target CreateMap<Command, CommandReadDto>(); } } }
private readonly ICommanderRepo _repository; private readonly IMapper _mapper; public CommandsController(ICommanderRepo repository, IMapper mapper) { _repository = repository; _mapper = mapper; }
//GET api/commands [HttpGet] public ActionResult<IEnumerable<CommandReadDto>> GetAllCommmands() { var commandItems = _repository.GetAllCommands(); return Ok(_mapper.Map<IEnumerable<CommandReadDto>>(commandItems)); }
代碼以下:
using System.ComponentModel.DataAnnotations; namespace Simple_Asp.Net_Core.Dtos { public class CommandCreateDto { [Required] [MaxLength(250)] public string HowTo { get; set; } [Required] public string Line { get; set; } [Required] public string Platform { get; set; } } }
using System.ComponentModel.DataAnnotations; namespace Simple_Asp.Net_Core.Dtos { public class CommandUpdateDto { [Required] [MaxLength(250)] public string HowTo { get; set; } [Required] public string Line { get; set; } [Required] public string Platform { get; set; } } }
代碼以下:
using AutoMapper; using Simple_Asp.Net_Core.Dtos; using Simple_Asp.Net_Core.Models; namespace Simple_Asp.Net_Core.Profiles { public class CommandsProfile : Profile { public CommandsProfile() { //Source -> Target CreateMap<Command, CommandReadDto>(); CreateMap<CommandCreateDto, Command>(); CreateMap<CommandUpdateDto, Command>(); CreateMap<Command, CommandUpdateDto>(); } } }
代碼以下:
using Simple_Asp.Net_Core.Models; using System.Collections.Generic; namespace Simple_Asp.Net_Core.Data { public interface ICommanderRepo { bool SaveChanges(); IEnumerable<Command> GetAllCommands(); Command GetCommandById(int id); void CreateCommand(Command cmd); void UpdateCommand(Command cmd); void DeleteCommand(Command cmd); } }
模擬倉儲層咱們就不作過多的實現,在下一章內容會與數據庫Postgresql進行對接,到時候再實現!
代碼以下:
using Simple_Asp.Net_Core.Models; using System.Collections.Generic; namespace Simple_Asp.Net_Core.Data { public class MockCommanderRepo : ICommanderRepo { public void CreateCommand(Command cmd) { throw new System.NotImplementedException(); } public void DeleteCommand(Command cmd) { throw new System.NotImplementedException(); } public IEnumerable<Command> GetAllCommands() { var commands = new List<Command> { new Command{Id=0, HowTo="Boil an egg", Line="Boil water", Platform="Kettle & Pan"}, new Command{Id=1, HowTo="Cut bread", Line="Get a knife", Platform="knife & chopping board"}, new Command{Id=2, HowTo="Make cup of tea", Line="Place teabag in cup", Platform="Kettle & cup"} }; return commands; } public Command GetCommandById(int id) { return new Command { Id = 0, HowTo = "Boil an egg", Line = "Boil water", Platform = "Kettle & Pan" }; } public bool SaveChanges() { throw new System.NotImplementedException(); } public void UpdateCommand(Command cmd) { throw new System.NotImplementedException(); } } }
代碼以下:
using AutoMapper; using Microsoft.AspNetCore.JsonPatch; using Microsoft.AspNetCore.Mvc; using Simple_Asp.Net_Core.Data; using Simple_Asp.Net_Core.Dtos; using Simple_Asp.Net_Core.Models; using System.Collections.Generic; namespace Simple_Asp.Net_Core.Controllers { [Route("api/[controller]")] [ApiController] public class CommandsController : ControllerBase { private readonly ICommanderRepo _repository; private readonly IMapper _mapper; public CommandsController(ICommanderRepo repository, IMapper mapper) { _repository = repository; _mapper = mapper; } //GET api/commands [HttpGet] public ActionResult<IEnumerable<CommandReadDto>> GetAllCommmands() { var commandItems = _repository.GetAllCommands(); return Ok(_mapper.Map<IEnumerable<CommandReadDto>>(commandItems)); } //GET api/commands/{id} [HttpGet("{id}", Name = "GetCommandById")] public ActionResult<CommandReadDto> GetCommandById(int id) { var commandItem = _repository.GetCommandById(id); if (commandItem != null) { return Ok(_mapper.Map<CommandReadDto>(commandItem)); } return NotFound(); } //POST api/commands [HttpPost] public ActionResult<CommandReadDto> CreateCommand(CommandCreateDto commandCreateDto) { var commandModel = _mapper.Map<Command>(commandCreateDto); _repository.CreateCommand(commandModel); _repository.SaveChanges(); var commandReadDto = _mapper.Map<CommandReadDto>(commandModel); return CreatedAtRoute(nameof(GetCommandById), new { Id = commandReadDto.Id }, commandReadDto); } //PUT api/commands/{id} [HttpPut("{id}")] public ActionResult UpdateCommand(int id, CommandUpdateDto commandUpdateDto) { var commandModelFromRepo = _repository.GetCommandById(id); if (commandModelFromRepo == null) { return NotFound(); } _mapper.Map(commandUpdateDto, commandModelFromRepo); _repository.UpdateCommand(commandModelFromRepo); _repository.SaveChanges(); return NoContent(); } //PATCH api/commands/{id} [HttpPatch("{id}")] public ActionResult PartialCommandUpdate(int id, JsonPatchDocument<CommandUpdateDto> patchDoc) { var commandModelFromRepo = _repository.GetCommandById(id); if (commandModelFromRepo == null) { return NotFound(); } var commandToPatch = _mapper.Map<CommandUpdateDto>(commandModelFromRepo); patchDoc.ApplyTo(commandToPatch); if (!TryValidateModel(commandToPatch)) { return ValidationProblem(ModelState); } _mapper.Map(commandToPatch, commandModelFromRepo); _repository.UpdateCommand(commandModelFromRepo); _repository.SaveChanges(); return NoContent(); } //DELETE api/commands/{id} [HttpDelete("{id}")] public ActionResult DeleteCommand(int id) { var commandModelFromRepo = _repository.GetCommandById(id); if (commandModelFromRepo == null) { return NotFound(); } _repository.DeleteCommand(commandModelFromRepo); _repository.SaveChanges(); return NoContent(); } } }
PUT 和 PATCH 方法用於更新現有資源。 它們之間的區別是,PUT會替換整個資源,而PATCH 僅指定更改。
參數以下:
[ { "op":"add", "path":"/Line", "value":"Barry" } ]
更多Patch語法說明能夠參考
https://docs.microsoft.com/zh-cn/aspnet/core/web-api/jsonpatch?view=aspnetcore-5.0
本文主要對Simple項目使用了AutoMapper與依賴注入等內容實現了簡單的Restful API開發。在實際開發過程當中須要根據不一樣的業務場景須要創建不一樣的Dto,不要由於偷懶讓相近的業務功能使用相同的Dto,這樣會讓後續的代碼維護成本變得更大!
目前針對AutoMpper的使用並非很是的便捷,後續能夠考慮進行提高。依賴注入使用的是自帶的方式實現,後續能夠結合第三方組件實現依賴注入
文中提到生命週期是依賴注入裏很是重要的內容,在實際開發過程當中要根據具體的業務狀況使用正確的生命週期!
注意:源碼調試過程當中若是出現xml文件路徑錯誤,須要參照第一章(後端項目搭建與Swagger配置步驟)Swagger配置「配置XML 文檔文件」步驟,取消勾選而後再選中 ,將XML路徑設置成與你的電腦路徑匹配!
官網文檔-依賴注入生命週期(推薦學習) https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes
Restful API 案例來源 https://www.youtube.com/watch?v=fmvcAzHpsk8
微軟官方文檔 https://docs.microsoft.com/zh-cn/aspnet/core/?view=aspnetcore-5.0
DTO理解 https://docs.microsoft.com/en-us/previous-versions/msp-n-p/ff649585(v=pandp.10)?redirectedfrom=MSDN
官網文檔-Patch請求詳解 https://docs.microsoft.com/zh-cn/aspnet/core/web-api/jsonpatch?view=aspnetcore-5.0