Skip to content

vaclavnovotny/NSwag.Examples

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

99 Commits
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Build and Publish Nuget

Response and Request Body Examples for NSwag

This library allows you to programmatically define swagger examples in your NSWag application. Example discovery occurs at start of application and uses reflection.

Overview:

Setup

Install package

Install-Package NSwag.Examples

Setup Startup.cs

Add services needed for example definition using AddExampleProviders() and provide assemblies where are your examples located.

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();

    services.AddExampleProviders(typeof(CityExample).Assembly);
    services.AddOpenApiDocument((settings, provider) =>
    {
        settings.AddExamples(provider);
    });
}

Define examples for your types

Create class and implement interface IExampleProvider where T is the type you want to define example for.

public class CityExample : IExampleProvider<City>
{
    public City GetExample()
    {
        return new City()
        {
            Id = 5,
            Name = "Brno",
            People = new List<Person>()
            {
                new Person() {Id = 1, FirstName = "Henry", LastName = "Cavill"},
                new Person() {Id = 2, FirstName = "John", LastName = "Doe"}
            }
        };
    }
}

Request Body Parameters

For request body parameters there is nothing you need to worry about, just mark your parameter [FromBody].

[HttpPost]
public async Task<IActionResult> CreatePerson([FromBody, BindRequired] Person person)
{
    // create person logic
    return Ok();
}

Result in swagger: Image of request body

Response Body

For response body types you need to decorate your method with [ProducesResponseType]

[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(Person))]
public async Task<IActionResult> GetPerson([FromRoute]int id)
{
    return Ok(new Person());
}

Result in swagger: Image of request body

Use dependency injection

You can also use dependency injection in constructor of your example provider class.

Constructor and GetExample method gets called when operation processors are executed - when swagger specification is being generated which is during first request on swagger.

public class PersonExample : IExampleProvider<Person>
{
    private readonly IPersonNameGenerator _nameGenerator;
    private readonly Random _random;

    public PersonExample(IPersonNameGenerator nameGenerator)
    {
        _nameGenerator = nameGenerator;
        _random = new Random(); 
    }

    public Person GetExample()
    {
        return new Person()
        {
            Id = _random.Next(1, 100),
            FirstName = _nameGenerator.GenerateRandomFirstName(),
            LastName = _nameGenerator.GenerateRandomLastName()
        };
    }
}

You can also combine examples by reusing IExampleProvider<T>. You can directly inject desired example providers into constructor and use them within your example provider.

public class PragueExample : IExampleProvider<City>
{
    private readonly IEnumerable<IExampleProvider<Person>> _peopleExamples;

    public PragueExample(IEnumerable<IExampleProvider<Person>> peopleExamples) {
        _peopleExamples = peopleExamples;
    }
    
    public City GetExample() {
        return new City {
            Id = 420,
            Name = "Prague",
            People = _peopleExamples.Select(x => x.GetExample()).ToList()
        };
    }
}

Multiple examples

To define multiple examples for request or response body, simply create more classes implementing IExampleProvider<T> multiple times. Here, to define multiple examples of type City, we create two classes that implement interface IExampleProvider<City>.

public class BrnoExample : IExampleProvider<City>
{
    public City GetExample()
    {
        return new City {
            Id = 5,
            Name = "Brno",
            People = new List<Person> {
                new Person {Id = 1, FirstName = "Henry", LastName = "Cavill"},
                new Person {Id = 2, FirstName = "John", LastName = "Doe"}
            }
        };
    }
}

public class PragueExample : IExampleProvider<City>
{
    public City GetExample() {
        return new City {
            Id = 420,
            Name = "Prague",
            People = new List<Person> {
                new Person {Id = 1, FirstName = "Henry", LastName = "Cavill"},
                new Person {Id = 2, FirstName = "John", LastName = "Doe"}
            }
        };
    }
}

Swagger then contains dropdown with these options: Multiple examples

Naming the examples

You can name examples so they do not show up in Swagger as Example 1, Example 2, etc. Simply annotate your example with ExampleAnnotation attribute and set Name property.

[ExampleAnnotation(Name = "Brno")]
public class BrnoExample : IExampleProvider<City>

Swagger then shows these names in dropdown: Named examples

Restricting examples to request or response content

Examples can be annotated to restrict their usage to either a request or a response body. This is useful when the same type may be used on both requests and responses, and you want to restrict examples without specifying specific examples.

[ExampleAnnotation(ExampleType = ExampleType.Request)]
public class RequestExample : IExampleProvider<City> {...}

[ExampleAnnotation(ExampleType = ExampleType.Response)]
public class ResponseExample : IExampleProvider<City> {...}

Set example for specific endpoint

It is possible to set specific implementation of IExampleProvider<T> to desired API endpoint. This may be handy when multiple endpoints return same type or if primitive type is returned but you need to specify specific value. To set specific example, simply annotate controllers method with EndpointSpecificExample attribute and provide type of the example class.

[HttpGet("{id}/age")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
[EndpointSpecificExample(typeof(PersonSpecificAgeExample))] // <-----
public IActionResult GetPersonAge([FromRoute] int id) {...}

public class PersonSpecificAgeExample : IExampleProvider<int>
{
    public int GetExample() => 69;
}

Specify that an example is specifically for a request or response

When setting a specific implementation of IExampleProvider<T> for a desired API endpoint, it may be desired to specify specifically whether this example applies to the request or response.

[HttpPost("age")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
[EndpointSpecificExample(typeof(PersonSpecificAge69Example), ExampleType = ExampleType.Response)] // <----- Prevents example from being used in the request
[EndpointSpecificExample(typeof(PersonSpecificAge50Example), ExampleType = ExampleType.Request)] // <----- Prevents example from being used in the response
public IActionResult GetPersonAge([FromBody] int id) {...}

// Included on response
public class PersonSpecificAge69Example : IExampleProvider<int>
{
    public int GetExample() => 69;
}

// Included on request
public class PersonSpecificAge50Example : IExampleProvider<int>
{
    public int GetExample() => 50;
}

Specify that an example is specifically for a specific response code

When setting a specific implementation of IExampleProvider<T> for a desired API endpoint, it may be desired to specify specifically whether this example applies to the request or response.

[HttpGet("{id}/age")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
[EndpointSpecificExample(typeof(PersonSpecificAgeExample), ExampleType = ExampleType.Response, ResponseStatusCode = StatusCodes.Status200OK)] // <----- Only shown for 200 responses
[EndpointSpecificExample(typeof(BadRequestExample), ExampleType = ExampleType.Response, ResponseStatusCode = StatusCodes.Status400BadRequest)] // <----- Only shown for 400 responses
public IActionResult GetPersonAge([FromRoute] int id) {...}

public class PersonSpecificAgeExample : IExampleProvider<int>
{
    public int GetExample() => 69;
}

public class BadRequestExample : IExampleProvider<string>
{
    public int GetExample() => "Oops! Invalid id format error.";
}

Set multiple examples for specific endpoint

It is possible to set multiple specific implementations of IExampleProvider<T> to desired API endpoint. This may be handy when multiple endpoints return same type or if primitive type is returned but you need to specify specific value. To set specific example, simply annotate controllers method with EndpointSpecificExample attribute and provide type of the example class.

[HttpGet("{id}/age")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(int))]
[EndpointSpecificExample(typeof(PersonSpecificAge69Example), typeof(PersonSpecificAge50Example))] // <-----
public IActionResult GetPersonAge([FromRoute] int id) {...}

// Included
public class PersonSpecificAge69Example : IExampleProvider<int>
{
    public int GetExample() => 69;
}

// Included
public class PersonSpecificAge50Example : IExampleProvider<int>
{
    public int GetExample() => 50;
}

// Excluded
public class PersonSpecificAge35Example : IExampleProvider<int>
{
    public int GetExample() => 35;
}

Polymorphism

To define specific examples of abstract classes that are returned or received in controller, simply implement IExampleProvider<T> where T is the type of abstract class. In example below, we have abstract class Animal which is returned as collection from GetAnimals endpoint. To specify examples for each implementation of Animal type, we create classes that implement IExampleProvider<Animal>.

[HttpGet]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<Animal>))]
public IActionResult GetAnimals() {...}

public abstract class Animal {...}

public class Monkey : Animal
{
    public int PoopsThrown { get; set; }
}

public class Sloth : Animal
{
    public uint YawnsCount { get; set; }
}

[ExampleAnnotation(Name = "Monkey")]
public class MonkeyExample : IExampleProvider<Animal>
{
    public Animal GetExample() => new Monkey { Age = 5, Name = "Harambe", PawnCount = 4, PoopsThrown = 18 };
}

[ExampleAnnotation(Name = "Sloth")]
public class SlothExample : IExampleProvider<Animal>
{
    public Animal GetExample() => new Sloth { Age = 18, Name = "Vence", PawnCount = 4, YawnsCount = 158 };
}

Swagger then shows all examples in response body with dropdown: Polymorphism single item response body

Or in case of enumerable in response body: Polymorphism in response body

Or in case of request body, Swagger shows dropdown of all defined examples: Polymorphism in request body

Note: Set your JsonSerializer settings TypeNameHandling to see discriminator. For example:

services.AddOpenApiDocument(
    (settings, provider) =>
    {
        settings.SerializerSettings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.Auto };
        settings.AddExamples(provider);
    });

Support

I personally use this NuGet in my projects, so I will keep this repository up-to-date. Any ideas for extending functionalities are welcome, so create an issue with proposal.

Did I save you some hours?

ko-fi