Nswag: Set Swagger response description via xml docs or attribute

Created on 16 Nov 2018  ·  21Comments  ·  Source: RicoSuter/NSwag

Update by @RicoSuter:
Documentation about how to specify the response descriptions with XML Docs.

I'm on a .NET Core 2.1 Web API project and trying to obtain a complete swagger documentation like Swagger PetStore Demo.
I've enabled the XML comments on my project, by adding the <GenerateDocumentationFile> tag on the .csproj file:

<GenerateDocumentationFile>true</GenerateDocumentationFile>

I'm using this syntax on my controllers:

```c#
[Authorize]
[ApiVersion("1.0")]
[Produces("application/json")]
[SwaggerTag("Anomalies", Description = "Anomalies management.")]
public class AnomaliesController : BaseController
{
///


/// Find anomaly by ID
///

/// ID of the anomaly to return
/// Successful operation
/// Invalid ID supplied
/// Anomaly not found
[HttpGet("{id:long}")]
[ProducesResponseType(typeof(api.Anomaly), (int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
[ProducesResponseType((int)HttpStatusCode.NotFound)]
public async Task> Get(long id)
{
return Mapper.Map(await Mediator.Send(new GetAnomalyByIdQuery() { Id = id }));
}
}


I also tried with `SwaggerResponse` attribute:

```c#
[SwaggerResponse(HttpStatusCode.OK, typeof(api.Anomaly), Description = "Successfull operation")]

but without any result.

This is what I get:

image

This is what I want, like in PetStore Demo (red circle are my missing values):

image

help wanted enhancement

Most helpful comment

Thanks @RicoSuter that works a treat.

For example, the following endpoint:

/// <summary>
/// Get employee
/// </summary>
/// <param name="id">The identifier of the employee that should be returned.</param>
/// <returns>
/// Returns the specified employee, if it exists and the current user has access to it.
/// </returns>
/// <remarks>
/// Returns the specified employee, if it exists and the current user has access to it. This can depend on any combination of user roles, cost centres, locations, organisation charts etc.
/// </remarks>
/// <response code="404">The specified employee does not exist, or the current user does not have access to it.</response>
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Employee))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
[HttpGet("{id:long}")]
public async Task<IActionResult> GetById(long id)
{
    var results = await _dbContext.Employees.FirstOrDefaultAsync(e => e.Id == id);
    if (results == null)
    {
        return NotFound();
    }

    return Ok(results);
}

gets rendered in OpenAPI 3 as:

image

I still need to generate sample data to use in the responses, but it's looking very good, so thanks again @RicoSuter !

All 21 comments

I think you can implement an own attribute which inherits from ProducesResponseType and exposes a Description property for now

For this we need to improve this code here: https://github.com/RSuter/NSwag/blob/86e06e49b529fef46e9683e75aa07ff1a7383837/src/NSwag.SwaggerGeneration/Processors/OperationResponseProcessorBase.cs#L98

=> read from xml docs <response> or something else...

But replacing ProducesResponseType with SwaggerResponse should work...

@RSuter

But replacing ProducesResponseType with SwaggerResponse should work...

It works! Thanks!

This is my adjusted code, based on the previous sample.

c# [Authorize] [ApiVersion("1.0")] [Produces("application/json")] [SwaggerTag("Anomalies", Description = "Anomalies management.")] public class AnomaliesController : BaseController { /// <summary> /// Find anomaly by ID /// </summary> /// <param name="id">ID of the anomaly to return</param> /// <response code="200">Successful operation</response> /// <response code="400">Invalid ID supplied</response> /// <response code="404">Anomaly not found</response> [HttpGet("{id:long}")] [SwaggerResponse(HttpStatusCode.OK, typeof(api.Anomaly), Description = "Successfull operation")] [SwaggerResponse(HttpStatusCode.BadRequest, typeof(ProblemDetails), Description = "Invalid ID supplied")] [SwaggerResponse(HttpStatusCode.NotFound, typeof(ProblemDetails), Description = "Anomaly not found")] public async Task<ActionResult<api.Anomaly>> Get(long id) { return Mapper.Map<api.Anomaly>(await Mediator.Send(new GetAnomalyByIdQuery() { Id = id })); } }

Note: ProblemDetails object is the ASP.NET Core implementation of RFC 7807 Problem Details for HTTP APIs, so it is a standard object that can be used to manage errors.

I'm using the following code in my example project under https://github.com/SeppPenner/NetCoreMQTTExampleIdentityConfig:

/// <summary>
/// Gets the claim by id. GET "api/claim/5".
/// </summary>
/// <param name="claimId">
/// The claim identifier.
/// </param>
/// <returns>
/// The <see cref="Task"/>.
/// </returns>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
[HttpGet("{claimId}")]
[SwaggerResponse(HttpStatusCode.OK, typeof(DtoReadUserClaim), Description = "Claim found.")]
[SwaggerResponse(HttpStatusCode.NotFound, typeof(int), Description = "Claim not found.")]
[SwaggerResponse(HttpStatusCode.InternalServerError, typeof(string), Description = "Internal server error.")]
[ProducesResponseType(typeof(DtoReadUserClaim), StatusCodes.Status200OK)]
[ProducesResponseType(typeof(int), StatusCodes.Status404NotFound)]
[ProducesResponseType(typeof(string), StatusCodes.Status500InternalServerError)]
public async Task<ActionResult<DtoReadUserClaim>> GetClaimById(long claimId)
{
    \\ ...
}

which renders to:
image

The red circles show the stuff that is missing:

  • Summary for the method, e.g. Find pet by id
  • Summary for the return value, e.g. Returns a single pet
  • Documentation of the parameter, e.g. ID of the pet to return

How can I get this to work?

P.S. The controller description works as excpected:

image

The red circles show the stuff that is missing:

  • Summary for the method, e.g. Find pet by id
  • Summary for the return value, e.g. Returns a single pet
  • Documentation of the parameter, e.g. ID of the pet to return

How can I get this to work?

P.S. The controller description works as excpected:

image

The same happened to me when moved to v12 and now on v13 of NSwag.

The same happened to me when moved to v12 and now on v13 of NSwag.

I don't remember when this happend, but I'm using the latest version of NSwag for sure.

This happens if you use the version 3 of Swagger (AddOpenApiDocument() method) as well.

I'm using NSwag 13.0.6, and it is using the XMLDOC markup, where the <summary /> tag gets written as the method sumary, the <returns /> tag gets written to the default successful response value, <remarks /> tag forms the body of the documentation, and <param name="" /> tags populate the parameter descriptions. You'll need to make sure you project is set to write the XML output, and NSwag seems to pick that up automatically. That covers most scenarios; the only place it currently falls down is writing a description for non-default return status codes (e.g. 404, 401), and providing example request/response bodies.

So in @SeppPenner 's case, you've got all the above, apart from the <remarks /> tag, so I'm guessing you don't have the <DocumentationFile /> property set in your .csproj

So in @SeppPenner 's case, you've got all the above, apart from the tag, so I'm guessing you don't have the property set in your .csproj

Ah, this might be the case... I will check that.

You can use the <response code=“...”>... xml docs tags to specify the response texts.

Thanks @RicoSuter that works a treat.

For example, the following endpoint:

/// <summary>
/// Get employee
/// </summary>
/// <param name="id">The identifier of the employee that should be returned.</param>
/// <returns>
/// Returns the specified employee, if it exists and the current user has access to it.
/// </returns>
/// <remarks>
/// Returns the specified employee, if it exists and the current user has access to it. This can depend on any combination of user roles, cost centres, locations, organisation charts etc.
/// </remarks>
/// <response code="404">The specified employee does not exist, or the current user does not have access to it.</response>
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Employee))]
[ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
[HttpGet("{id:long}")]
public async Task<IActionResult> GetById(long id)
{
    var results = await _dbContext.Employees.FirstOrDefaultAsync(e => e.Id == id);
    if (results == null)
    {
        return NotFound();
    }

    return Ok(results);
}

gets rendered in OpenAPI 3 as:

image

I still need to generate sample data to use in the responses, but it's looking very good, so thanks again @RicoSuter !

@RicoSuter The example from @davidkeaveny works perfectly. In my case, I forgot the documentation file. I guess, you can close this issue.

I believe it would still be beneficial to use ProducesResponseType attribute.
1 - it's supplied with ASP.NET
2 - it doesn't force me to specify the return type two times
Also, in most cases I'm okay with just showing the default status code description and overriding it with a custom description is not desired.

Plus, neither XML comments nor SwaggerResponse work with web API conventions

        /// <summary>
        /// Returns the requested Contract with the given Id
        /// </summary>
        /// <remarks>*Requires Authorization</remarks>
        /// <returns>A Contract</returns>
        /// <response code="200">Request completed Successfully</response>
        /// <response code="400">Bad Request</response>
        /// <response code="401">Unathorized Request</response>
        /// <response code="500">Interal Error has occured</response>
        [HttpGet("{id}")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(typeof(Exception), StatusCodes.Status401Unauthorized)]
        [ProducesResponseType(typeof(Exception), StatusCodes.Status400BadRequest)]
        [ProducesResponseType(typeof(Exception), StatusCodes.Status500InternalServerError)]

So this is an example block of code that gets repeated around 100 times in our codebase.

I managed to move all the ProducesResponseType to our custom convention and apply it to the whole codebase through 1 cmd.

However because the ProducesResponseType doesnt contain any field for Description we have to use the xml docs to provide the description. I am not sure why .NetCore doesnt include a description field.

Is anyone else with the same issue?

Yeah this is currently a limitation. You can implement a global custom operation processor (IOperationProcessor) which adds the descriptions to all operations with the convention.

Or use an xmlinclude (one line) and load the xml descs from a single xml file

https://blog.rsuter.com/how-to-write-detailed-c-xml-documentation-and-still-keep-the-source-code-clean/

Does the data gets initialized with default values ? Because that's what I get. Is there a way to show the values I set?
<example> { "Id": "1", "Name": "Name1" } </example>
On UI rather than giving me the values it shows 0 and string.

@davidkeaveny

So in @SeppPenner 's case, you've got all the above, apart from the <remarks /> tag, so I'm guessing you don't have the <DocumentationFile /> property set in your .csproj

I was losing my mind trying to figure out why none of my XML summaries were showing up in the exported json file or in the web ui. The comment above here led me to the answer. My solution was to:

Add the following dependencies:

  • System.IO.FileSystem
  • System.Xml.XPath.XDocument

Add this line to the csproj file.

<GenerateDocumentationFile>true</GenerateDocumentationFile>

For me, I got hung up on the fact that simply setting a DocumentationFile won't cut it. It needs to be named correctly and in the proper place for it to be picked up.

(From https://github.com/RicoSuter/NJsonSchema/wiki/XML-Documentation#net-core )

Was this page helpful?
0 / 5 - 0 ratings