Giter Club home page Giter Club logo

nswag.examples's Introduction

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

nswag.examples's People

Contributors

vaclavnovotny avatar baltika10 avatar davidkeaveny avatar hampzter avatar navikannur avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.