Post 使用模型 class 与 DTO 的操作
Post Action with model class vs DTO
此 MSDN link 解释了为什么在 Web API 中使用 DTO classes 是一个很好的做法。这是可以理解的,让我感到困惑的是在同一页中,post 方法使用模型 class 而不是简单的 DTO,如下所示:
[ResponseType(typeof(BookDTO))]
public async Task<IHttpActionResult> PostBook(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Books.Add(book);
await db.SaveChangesAsync();
// New code:
// Load author name
db.Entry(book).Reference(x => x.Author).Load();
var dto = new BookDTO()
{
Id = book.Id,
Title = book.Title,
AuthorName = book.Author.Name
};
return CreatedAtRoute("DefaultApi", new { id = book.Id }, dto);
}
我想我的问题是:action Post/Put 应该采用模型还是 DTO 参数?
更新:
从答案来看,即使在 post/put 操作的情况下,看起来也建议使用 dto,但这将导致另一个问题,如何在 post 操作的情况下从 dto 映射到模型 class ?假设我们使用 AutoMapper,我发现许多链接如 this and this 警告不要在反向映射中使用它(即 dto => 模型 class)。
对于我的个人项目和工作中的项目,我制定的约定是我们将使用附加了 Request
或 Response
的 DTO 对象来帮助维护和清洁 API 表面上大家都能看懂。
public CreateBookResponse CreateBook(CreateBookRequest request)
{
}
此外,在某些情况下,我们根据 Web 服务的需要使用 HttpResponseMessage
、IActionResult
或其他 return 值,但具有请求和响应对象明确定义使我们所有的开发人员,即使是像 ASP 这样的不同技能的开发人员,也很容易理解由此产生的 JSON 结构。
我建议不要直接公开业务实体或数据访问对象。每一个在您的应用程序架构中都有不同的目的,您可能会暴露太多细节,或者发现维护横切关注点很困难。
首先,是的,您应该始终使用 DTO。无论您处理的是常规网站还是 API,您都应该 永远不要 直接保存从 post 数据实例化的对象。这打开了一个巨大的安全漏洞,人们可以在其中操纵 post 数据并进行各种恶作剧。具有讽刺意味的是,你实际上可以绑定到你的实体 class,但如果你这样做,你不应该保存那个实例,而是创建一个新实例,映射来自 posted 实例的数据,然后保存您创建的那个实例 - 重要的部分永远不会保存 posted 实例。使用视图 model/DTO 只会让你更清楚地知道你应该做等式的映射部分,因此这是推荐的方法。
就 AutoMapper 而言,建议不要用于反向映射,因为在映射到将要保存到数据库的内容时涉及许多细微差别。特别是当您涉及 Entity Framework 这样的 ORM 时。您可以使用 AutoMapper,但您只需要了解所有这些细微差别并相应地处理它们。一般来说,在这些场景中手动进行映射可能更容易,无论如何,因为它通常涉及 AutoMapper 之类的大量配置,以至于您在漫长的 运行 中并没有节省太多精力。手动映射就像听起来一样。如果您要创建一个新的 Book
,那么您只需新建一个 Book
的实例。如果您正在修改现有的 Book
,您会从数据库中提取它的一个实例。无论哪种方式,您现在都有一个 Book
的实例和一个类似 BookDTO
的实例,它们是从 post 数据创建的。那么你只需:
book.Title = bookDto.Tile;
// etc.
对于作者关系之类的内容,您可能需要进行额外的查询。例如:
var author = _context.Authors.SingleOrDefault(x => x.Name == bookDto.AuthorName);
book.Author = author;
简答:由您决定。
更长的答案:我想将 DTO 视为层(例如服务)在 simple/flat 数据结构中从外部接收数据或向外部公开数据的方式。根据这个定义,Web Api 中的模型只是一种 DTO,您可以在其中添加验证属性(例如 Required、MinLength 等),并且 api 控制器可以为您进行验证通过检查 ModelState.IsValid (及其错误)。但您也可以在 DTO class 中添加这些属性。所以其实差别不大。
此 MSDN link 解释了为什么在 Web API 中使用 DTO classes 是一个很好的做法。这是可以理解的,让我感到困惑的是在同一页中,post 方法使用模型 class 而不是简单的 DTO,如下所示:
[ResponseType(typeof(BookDTO))]
public async Task<IHttpActionResult> PostBook(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
db.Books.Add(book);
await db.SaveChangesAsync();
// New code:
// Load author name
db.Entry(book).Reference(x => x.Author).Load();
var dto = new BookDTO()
{
Id = book.Id,
Title = book.Title,
AuthorName = book.Author.Name
};
return CreatedAtRoute("DefaultApi", new { id = book.Id }, dto);
}
我想我的问题是:action Post/Put 应该采用模型还是 DTO 参数?
更新: 从答案来看,即使在 post/put 操作的情况下,看起来也建议使用 dto,但这将导致另一个问题,如何在 post 操作的情况下从 dto 映射到模型 class ?假设我们使用 AutoMapper,我发现许多链接如 this and this 警告不要在反向映射中使用它(即 dto => 模型 class)。
对于我的个人项目和工作中的项目,我制定的约定是我们将使用附加了 Request
或 Response
的 DTO 对象来帮助维护和清洁 API 表面上大家都能看懂。
public CreateBookResponse CreateBook(CreateBookRequest request)
{
}
此外,在某些情况下,我们根据 Web 服务的需要使用 HttpResponseMessage
、IActionResult
或其他 return 值,但具有请求和响应对象明确定义使我们所有的开发人员,即使是像 ASP 这样的不同技能的开发人员,也很容易理解由此产生的 JSON 结构。
我建议不要直接公开业务实体或数据访问对象。每一个在您的应用程序架构中都有不同的目的,您可能会暴露太多细节,或者发现维护横切关注点很困难。
首先,是的,您应该始终使用 DTO。无论您处理的是常规网站还是 API,您都应该 永远不要 直接保存从 post 数据实例化的对象。这打开了一个巨大的安全漏洞,人们可以在其中操纵 post 数据并进行各种恶作剧。具有讽刺意味的是,你实际上可以绑定到你的实体 class,但如果你这样做,你不应该保存那个实例,而是创建一个新实例,映射来自 posted 实例的数据,然后保存您创建的那个实例 - 重要的部分永远不会保存 posted 实例。使用视图 model/DTO 只会让你更清楚地知道你应该做等式的映射部分,因此这是推荐的方法。
就 AutoMapper 而言,建议不要用于反向映射,因为在映射到将要保存到数据库的内容时涉及许多细微差别。特别是当您涉及 Entity Framework 这样的 ORM 时。您可以使用 AutoMapper,但您只需要了解所有这些细微差别并相应地处理它们。一般来说,在这些场景中手动进行映射可能更容易,无论如何,因为它通常涉及 AutoMapper 之类的大量配置,以至于您在漫长的 运行 中并没有节省太多精力。手动映射就像听起来一样。如果您要创建一个新的 Book
,那么您只需新建一个 Book
的实例。如果您正在修改现有的 Book
,您会从数据库中提取它的一个实例。无论哪种方式,您现在都有一个 Book
的实例和一个类似 BookDTO
的实例,它们是从 post 数据创建的。那么你只需:
book.Title = bookDto.Tile;
// etc.
对于作者关系之类的内容,您可能需要进行额外的查询。例如:
var author = _context.Authors.SingleOrDefault(x => x.Name == bookDto.AuthorName);
book.Author = author;
简答:由您决定。
更长的答案:我想将 DTO 视为层(例如服务)在 simple/flat 数据结构中从外部接收数据或向外部公开数据的方式。根据这个定义,Web Api 中的模型只是一种 DTO,您可以在其中添加验证属性(例如 Required、MinLength 等),并且 api 控制器可以为您进行验证通过检查 ModelState.IsValid (及其错误)。但您也可以在 DTO class 中添加这些属性。所以其实差别不大。