简体   繁体   English

使用模型类与 DTO 发布操作

[英]Post Action with model class vs DTO

This MSDN link explains why it is a good practice to use DTOs classes for web API.这个 MSDN 链接解释了为什么将 DTO 类用于 Web API 是一种很好的做法。 This is understandable, what confuses me is in the same page, the post method uses the model class instead of the simple DTO one as following:这是可以理解的,让我困惑的是在同一页面中,post 方法使用模型类而不是简单的 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);
}

I guess my question is: Should Post/Put action takes model or DTO parameter?我想我的问题是: Post/Put 操作应该采用模型还是 DTO 参数?

Update : From answers, it looks like using dto is recommended even in case of post/put action, this will lead to another question though, how to map from dto to Model class in case of post action?更新:从答案来看,即使在 post/put action 的情况下,似乎也建议使用 dto,但这会导致另一个问题,如何在 post action 的情况下从 dto 映射到 Model 类? Let's say we use AutoMapper, I found many links such as this and this warn against using it in reverse mapping (ie dto => Model class).假设我们使用 AutoMapper,我发现了很多链接,例如thisthis警告不要在反向映射中使用它(即 dto => Model 类)。

First, yes, you should always use a DTO.首先,是的,您应该始终使用 DTO。 Whether you're dealing with a regular website or an API, you should never directly save an object instantiated from post data.无论你正在处理一个正规的网站或API,你永远不应该直接保存对象从POST数据实例化。 This opens a huge security hole where people can manipulate the post data and wreak all sorts of mischief.这打开了一个巨大的安全漏洞,人们可以在其中操纵帖子数据并进行各种恶作剧。 Ironically, you can actually bind to your entity class, but if you do, you should never save that instance, but rather create a new instance, map over the data from the posted instance, and then save that instance you created instead - the important part is never saving the posted instance.具有讽刺意味的是,您实际上可以绑定到您的实体类,但如果您这样做了,您不应该保存该实例,而是创建一个新实例,映射来自已发布实例的数据,然后保存您创建的实例 - 重要的是部分从不保存发布的实例。 Using a view model/DTO just makes it more obvious that you should do the mapping part of the equation, and as such is the recommended approach.使用视图模型/DTO 只会使您更明显地应该执行等式的映射部分,因此是推荐的方法。

As far as AutoMapper goes, it's recommended to not use for reverse mapping because there are numerous nuances involved in mapping to something that's going to be saved to a database.就 AutoMapper 而言,建议不要用于反向映射,因为映射到将要保存到数据库的内容涉及许多细微​​差别。 Particular when you have ORMs like Entity Framework involved.特别是当您涉及实体框架等 ORM 时。 You can use AutoMapper, but you just need to be aware of all these nuances and handle them accordingly.您可以使用 AutoMapper,但您只需要了解所有这些细微差别并相应地处理它们。 Generally speaking, it's probably easier in these scenarios to do the mapping manually, anyways, as it usually involves so much configuration for something like AutoMapper that you're not saving yourself much effort in the long run.一般来说,无论如何,在这些情况下手动进行映射可能更容易,因为它通常涉及 AutoMapper 之类的配置,从长远来看,您不会为自己节省太多精力。 Manual mapping is just as it sounds.手动映射就像听起来一样。 If you're creating a new Book , then you just new up an instance of Book .如果您正在创建一个新Book ,那么您只需新建一个Book实例。 If you're modifying an existing Book you pull an instance of that from the database.如果您正在修改现有Book ,则从数据库中提取该Book的实例。 Either way, you now have an instance of Book and an instance of something like BookDTO , which was created from the post data.无论哪种方式,您现在都有一个Book实例和一个类似BookDTO的实例,它是从 post 数据创建的。 Then you just:那么你只需:

book.Title = bookDto.Tile;
// etc.

For something like an author relationship, you may need to do additional queries.对于诸如作者关系之类的事情,您可能需要进行额外的查询。 For example:例如:

var author = _context.Authors.SingleOrDefault(x => x.Name == bookDto.AuthorName);
book.Author = author;

For my personal projects and projects at work, the conventions I put in place are that we are to use DTO objects appended with either Request or Response to aid in maintainability and a clean API surface that is understandable for everyone.对于我的个人项目和工作中的项目,我制定的约定是我们将使用 DTO 对象附加RequestResponse以帮助可维护性和每个人都可以理解的干净 API 表面。

public CreateBookResponse CreateBook(CreateBookRequest request)
{
}

Additionally, there are certain circumstances where we use HttpResponseMessage , IActionResult , or other return values depending on the need of the web service, but having the request and response objects clearly defined make it very easy for all of our developers, even of different skills like ASP, to understand the resulting JSON structures.此外,在某些情况下,我们根据 Web 服务的需要使用HttpResponseMessageIActionResult或其他返回值,但是明确定义请求和响应对象使我们所有的开发人员都非常容易,即使是具有不同技能的开发人员,例如ASP,以了解生成的 JSON 结构。

I would advise against exposing business entities or data access objects directly.我建议不要直接公开业务实体或数据访问对象。 Each of these have a different purpose in your application architecture, and you may be exposing too many details, or find maintaining cross-cutting concerns is difficult.这些中的每一个在您的应用程序架构中都有不同的用途,您可能会暴露太多细节,或者发现维护横切关注点很困难。

Short answer: up to you.简短的回答:取决于你。

Longer answer: I would like to think DTO as way for a layer (eg a service) receiving data from or exposing data to outside in a simple/flat data structure.更长的答案:我想将 DTO 视为层(例如服务)以简单/平面数据结构从外部接收数据或向外部公开数据的方式。 By this definition then the model in Web Api is just a type of DTO, within which you can add validation attributes (eg Required, MinLength etc) with the convenience that api controller can do the validation for you by checking ModelState.IsValid (and it's errors).根据这个定义,Web Api 中的模型只是一种 DTO,您可以在其中添加验证属性(例如,Required、MinLength 等),方便 api 控制器可以通过检查 ModelState.IsValid 为您进行验证(它是错误)。 But you can also add those attributes in a DTO class.但是您也可以在 DTO 类中添加这些属性。 So it's really not much difference.所以其实差别不大。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM