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).
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;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With