《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