Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Post Action with model class vs DTO

This MSDN link explains why it is a good practice to use DTOs classes for 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:

[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?

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? Let's say we use AutoMapper, I found many links such as this and this warn against using it in reverse mapping (i.e. dto => Model class).

like image 345
Mostafa abdo Avatar asked Aug 27 '18 02:08

Mostafa abdo


1 Answers

First, yes, you should always use a DTO. Whether you're dealing with a regular website or an API, you should never directly save an object instantiated from post data. 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.

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. Particular when you have ORMs like Entity Framework involved. You can use AutoMapper, but you just need to be aware of all these nuances and handle them accordingly. 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. Manual mapping is just as it sounds. If you're creating a new Book, then you just new up an instance of Book. If you're modifying an existing Book you pull an instance of that from the database. Either way, you now have an instance of Book and an instance of something like BookDTO, which was created from the post data. 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;
like image 57
Chris Pratt Avatar answered Sep 25 '22 14:09

Chris Pratt