I try to improve myself with .NET Web API now and I am trying to return a custom error in Swagger. But when returning this custom error, I can see the error is on which line. How can I do to prevent this?
public async Task<BookCreateDTO> CreateBook(BookCreateDTO bookCreateDto)
{
if (await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name))
{
throw new BookExistException("Book already exist");
}
var book= _mapper.Map<Book>(bookCreateDto);
_context.Books.Add(book);
await _context.SaveChangesAsync();
return book;
}
What should I do to see only this exception message in the Swagger response?
Thank you for your help.
>Solution :
Exceptions should be exceptional: Don’t throw exceptions for non-exceptional errors.
I don’t recommend specifying your web-service’s response DTO type in the C# action method return type because it limits your expressiveness (as you’re discovering).
- Instead use
IActionResultorActionResult<T>to document the default (i.e. HTTP 2xx) response type and then list error DTO types in[ProducesResponseType]attributes with their corresponding HTTP status codes.- This also means that each response status code should only be associated with a single DTO type. This is a limitation of Swagger (which does not allow you to say "if the response status is HTTP 200 then the response body/DTO is one-of
DtoFoo,DtoBar,DtoQux", unfortunately)
- This also means that each response status code should only be associated with a single DTO type. This is a limitation of Swagger (which does not allow you to say "if the response status is HTTP 200 then the response body/DTO is one-of
- For error conditions, add the errors to
ModelState(with theKey, if possible) and let ASP.NET Core handle the rest for you withProblemDetails. - Document the DTOs used, and their corresponding HTTP status codes, with
[ProducesResponseType]: this is very useful when using Swagger/NSwag to generate online documentation and client libraries. - Also: do not use EF entity types as DTOs or ViewModels.
- The main reason is that when the response is serialized, entities with lazy-loaded properties will cause your entire database object-graph to be serialized (bad!)
- Another reason is security: if you directly accept an EF entity as an input request body DTO or HTML form model then users/visitors can set properties arbitrarily, e.g.
POST /userswith{ accessLevel: 'superAdmin' }, for example. - Another is intent: an entity-type is for in-proc state, not as a communications contract.
[ProducesResponseType(typeof(BookCreateDTO), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(ProblemDetails), StatusCodes.Status409Conflict)]
public async Task< ActionResult<BookCreateDTO> > CreateBook( [FromBody] BookCreateDTO bookCreateDto )
{
// Does a book with the same name exist? If so, then return HTTP 409 Conflict.
if( await _context.Books.AnyAsync(x => x.Name == bookCreateDto.Name) )
{
this.ModelState.Add( nameof(BookCreateDTO.Name), "Book already exists" );
BadRequestObjectResult conflictResult = this.BadRequest( this.ModelState );
// `BadRequestObjectResult` is HTTP 400 by default, change it to HTTP 409:
conflictResult.StatusCode = 409;
return conflictResult;
}
Book addedBook;
{
addedBook = this.mapper.Map<Book>( bookCreateDto );
_ = this.context.Books.Add( book );
_ = await this.context.SaveChangesAsync();
}
BookCreateDTO responseDto = this.mapper.Map<BookCreateDTO >( addedBook );
return this.Ok( responseDto );
}