《Asp.Net Core3 + Vue3入坑教程》 - 3.AutoMapper & Restful API & DI

簡介

《Asp.Net Core3 + Vue3入坑教程》 此教程適合新手入門或者先後端分離嘗試者。能夠根據圖文一步一步進操做編碼也能夠選擇直接查看源碼。每一篇文章都有對應的源碼html

教程後期會將 .Net Core 3升級成 .Net Core 5前端

目錄

《Asp.Net Core3 + Vue3入坑教程》系列教程目錄

Asp.Net Core後端項目vue

  1. 後端項目搭建與Swagger配置步驟
  2. 配置CROS策略解決跨域問題
  3. (本文)AutoMapper & Restful API & DI
  4. (暫未發表敬請期待...)EF Core & Postgresql
  5. (暫未發表敬請期待...).Net Core 3升級成 .Net Core 5
  6. (暫未發表敬請期待...)JWT

Vue3 前端項目git

暫未發表敬請期待...github

本文簡介

本文爲《Asp.Net Core3 + Vue3入坑教程》系列教程的後端第三篇 - AutoMapper & Restful API & DI。本文將利用AutoMapper與依賴注入等內容實現一個簡單的Restful API。web

實現一個簡單的Restful API

引入NewtonsoftJson3.1.12版本的Nuget包

當前項目使用的SKD是 .net core 3後續將SDK升級以後再升級此Nuget包的版本sql

配置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.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());
        }
    }
}

新建文件夾Models,新建實體Command.cs


代碼以下: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; }
    }
}

新建Data文件夾 新建 ICommanderRepo 倉儲層接口,用來定義與數據庫交互的接口

using Simple_Asp.Net_Core.Models;
using System.Collections.Generic;

namespace Simple_Asp.Net_Core.Data
{
    public interface ICommanderRepo
    {
        IEnumerable<Command> GetAllCommands();
    }
}

如今咱們尚未數據庫,就先模擬數據返回!新建MockCommanderRepo.cs來實現ICommanderRepo接口

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;
        }
    }
}

新建Dtos文件夾,新建類CommandReadDto.cs

上一步模擬實現了從數據庫返回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; }
    }
}

實現ICommanderRepo的依賴注入

生命週期是依賴注入裏很是重要的內容,具體能夠參照官方的文檔
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());
        }
    }
}

在編寫Restful API以前還差最後一步,AutoMapper的使用

前面已經建立了Command實體與CommandReadDto Dto,如今咱們要讓這Commond實體可以自動轉換成CommandReadDto Dto

AutoMapper引入Nuget包

再一次配置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.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());
        }
    }
}

新建Profiles文件夾,新建CommandsProfile.cs AutoMpper映射配置類

代碼以下:

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>();
        }
    }
}

在Controllers文件夾下新建控制器CommandsController.cs

將接口注入至 CommandsController 構造函數中

private readonly ICommanderRepo _repository;
private readonly IMapper _mapper;
public CommandsController(ICommanderRepo repository, IMapper mapper)
{
   _repository = repository;
   _mapper = mapper;
}

這時候咱們就能夠實現 Commands 中 api/commands 請求

//GET api/commands
[HttpGet]
public ActionResult<IEnumerable<CommandReadDto>> GetAllCommmands()
{
    var commandItems = _repository.GetAllCommands();

    return Ok(_mapper.Map<IEnumerable<CommandReadDto>>(commandItems));
}

調試項目使用swagger調用api/commands接口,後端可以成功返回數據!


接下來就完成剩下的幾種請求

在Dtos文件夾下新建CommandUpdateDto.cs 與 CommandCreateDto.cs

代碼以下:

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; }
    }
}

修改CommandsProfile.cs

代碼以下:

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>();
        }
    }
}

修改ICommanderRepo.cs

代碼以下:

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);
    }
}

修改MockCommanderRepo.cs

模擬倉儲層咱們就不作過多的實現,在下一章內容會與數據庫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();
        }
    }
}

修改CommandsController.cs

代碼以下:

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();
        }
    }
}

實現HttpPatch的時候須要引入JsonPath Nuget包,能夠直接如圖所示直接引入,也可使用Nuget包管理界面進行引入

調試項目,能夠看到Restful API 已開發完成!

重點說明 HttpPatch 請求

PUT 和 PATCH 方法用於更新現有資源。 它們之間的區別是,PUT會替換整個資源,而PATCH 僅指定更改

接來下咱們用swagger來驗證


參數以下:

[
{
    "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的使用並非很是的便捷,後續能夠考慮進行提高。依賴注入使用的是自帶的方式實現,後續能夠結合第三方組件實現依賴注入

文中提到生命週期是依賴注入裏很是重要的內容,在實際開發過程當中要根據具體的業務狀況使用正確的生命週期!

GitHub源碼

注意:源碼調試過程當中若是出現xml文件路徑錯誤,須要參照第一章(後端項目搭建與Swagger配置步驟)Swagger配置「配置XML 文檔文件」步驟,取消勾選而後再選中 ,將XML路徑設置成與你的電腦路徑匹配!

https://github.com/Impartsoft/Simple_Asp.Net_Core/tree/master/Simple_Asp.Net_Core 3.AutoMapper %26 Restful API

參考資料

官網文檔-依賴注入生命週期(推薦學習) 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

相關文章
相關標籤/搜索