什么是DTO?
.Net DTO是一个对象,它定义了数据如何在网络上发送。它只用于发送和接收数据,不包含任何业务逻辑。使用DTO的原因有以下几个:
- 将服务层与数据库层分离
- 隐藏客户端不需要查看的特定属性
- 省略一些属性以减少有效负载大小
- 处理嵌套对象,使其更方便客户端使用
- 避免“过度提交”漏洞
什么是过度提交?
过度提交是一种攻击,它利用了MVC的模型绑定功能,向服务器发送额外的数据,以修改不应该被改变的模型属性。例如,如果你有一个表示用户的模型,它包含了用户名、密码和角色等属性,你可能只想让用户在表单中修改自己的密码,而不是角色。但是,如果你不限制模型绑定的范围,一个恶意的用户可以在表单中添加一个角色字段,以改变自己或其他用户的角色。这样就可能导致权限提升、数据泄露或其他安全问题。
什么是AutoMapper
AutoMapper是一个简单的库,用于解决一个看似复杂的问题——消除对象之间的映射代码。AutoMapper可以根据约定来匹配源对象和目标对象的属性,自动将一个类型的对象转换为另一个类型的对象。AutoMapper适用于模型投影的场景,可以将复杂的对象模型展平为DTO或其他简单的对象,这些对象的设计更适合序列化、通信、消息传递或作为域和应用层之间的防腐层。
AutoMapper优缺点:
AutoMapper有以下一些优点:
- 它可以节省你编写映射代码的时间,因为它可以根据约定来自动匹配源对象和目标对象的属性。
- 它可以让你的映射配置更简洁,因为它提供了一些配置选项和扩展方法来自定义映射规则。
- 它可以让你的映射代码更易于测试,因为它提供了一些测试工具和断言方法来验证映射是否正确。
AutoMapper也有以下一些缺点:
- 它会降低代码的可导航性,因为你无法通过工具来查找属性的赋值或使用位置,只能通过运行时的反射来确定映射关系。
- 它会增加重构的风险,因为如果你修改了源对象或目标对象的属性名称或类型,编译器不会报错,但运行时可能会出现异常或错误的映射结果。
- 它会违背领域驱动开发的原则,因为它鼓励你使用相同或类似的属性名称和类型来实现自动映射,而不是根据领域的需求来设计你的对象模型。
- 它需要额外的映射验证,因为你不能仅仅依赖于约定来保证映射的正确性,你还需要编写测试或使用验证方法来检查映射配置是否有效。
AutoMapper例子:
如果将Model层的实体类作为传输对象,发送给前端时是以Json的格式发送,避免不了会将下面的红色代码也进行Json格式化。前端发送的数据也必须携带List<Book>的Json数据,这样就会很麻烦。
public class BookType { public int Id { get; set; } public string TypeName { get; set; } public List<Book>? Books { get; set; } } |
public class Book { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedDate { get; set; }= DateTime.Now; public string? Author { get; set; } public int? BookTypeId { get; set; } public BookType? BookType { get; set; } } |
使用AutoMapper框架进行DTO映射
项目目录
在Web Api项目中安装Nuget包
AutoMapper.Extensions.Microsoft.DependencyInjection |
在DTO类库中安装Nuget包
AutoMapper |
在DTO类库创建DTO类
public class BookDto { public int Id { get; set; } public string Name { get; set; } public DateTime CreatedDate { get; set; }= DateTime.Now; public string? Author { get; set; }
public int? BookTypeId { get; set; } }
public class BookTypeDto { public int Id { get; set; } public string TypeName { get; set; } } |
在DTO类库中创建一个类继承Profile
public class BookInfoProfile : Profile { public BookInfoProfile() { CreateMap<BookDto, Book>(); CreateMap<BookType, BookTypeDto>(); } } |
在Web Api项目的Program文件配置AutoMapper
builder.Services.AddAutoMapper(typeof(BookInfoProfile)); |
控制器使用AutoMapper映射
[Route("api/[controller]/[action]")] [ApiController] public class BookController : ControllerBase { private MyContext context; private IMapper mapper; public BookController(MyContext context, IMapper mapper) { this.context = context; this.mapper = mapper; }
//添加一个Book数据 [HttpPost] public ActionResult<Book> PostAddBook(BookDto book) { //将BookDto映射为Book,之后添加至数据库中 Book book1 = mapper.Map<Book>(book); context.Books.Add(book1); context.SaveChanges(); return book1; }
} |