I'm using automatically generated Swagger API definitions in some of my projects, such as AVACloud and Dangl.Identity. Against best practices, I've got some controllers that might return different types of response objects. For example, I've developed LightQuery to enablie pagination and sorting in ASP.NET API controllers with the single addition of a [LightQuery] attribute.
This means that, depending on the request, the response might be a simple JSON array of objects or it might be paginated and wrapped in a response container with information about the current subset, similar to how OData structures its responses.
On top of that, I'm a big fan of doing as little work as possible. In the context of web APIs, this means I'll use NSwag on the server side to automatically generate Swagger API definitions, which I subsequently use to drive the automatic generation of, for example, .NET or TypeScript clients for libraries and web frontends. To facilitate that, I'm annotating my controllers with a bit of metadata, such as the [ProducesResponseType] attribute.
As you can see, I've used the attribute twice - once to indicate the plain response, a second time to state what the paginated response will look like. Up until NSwag v11.19, this produced correct code - both schemas were included in the action descriptor of the API specification. This meant that my .NET clients did type the response as object and I had to handle it at the consumer side.
However, after upgrading NSwag to v11.20, I've noticed that it was only picking up the first defined response type, thus simply ignoring all others:
For obvious reasons, this is bad - the generated spec did not match with the actual behavior of my server. After doing a bit of digging around, I did not find anything about this change being intentional. However, I've also discovered that you should preferably use the attributes shipped in the NSwag.Annotations namespace.
Swapping the attribute leads to this action definition:
which promptly produces correct API definitions again:
You can spot the difference in the generated .NET / C# client, where it now correctly produces the response as object again:
Finally, to support my striving for having to do as little work as possible, I've added two tests to ensure I'll never run into this specific problem again.
The first test requests the generated Swagger specification from the In-Memory TestHost instance and just ensures that a particular action I know to have two response schemas actually has two schemas. The second one is just a safeguard - I'm using reflection to ensure I do never use two [ProducesResponseType] attributes on the same controller action to define valid responses.
In summary, I'm really happy with automatically generating Swagger specifications, but I'm learning something new about it every week. If you're relying on such generated documents, I'd recommend you have good integration tests for your projects and use them. Changes might come in suddenly when doing package upgrades without you necessarily noticing them early.