Generating Swagger example requests with Swashbuckle

This is a follow on from my post from last year about Generating example Swagger responses.

Update May 4th 2017: I have created a new NuGet package called Swashbuckle.Examples which contains the functionality I previously described in this blog post. The code lives on GitHub.

I have also created a .NET Standard version of the NuGet package at Swashbuckle.AspNetCore.Examples, which is also on GitHub.

It can also be useful to generate example requests, and in this post I will show you how.

First, install my Swashbuckle.Examples NuGet package.

Now decorate your controller methods with the included SwaggerRequestExample attribute:

[Route(RouteTemplates.DeliveryOptionsSearchByAddress)]
[SwaggerRequestExample(typeof(DeliveryOptionsSearchModel), typeof(DeliveryOptionsSearchModelExample))]
[SwaggerResponse(HttpStatusCode.OK, Type = typeof(DeliveryOptionsModel), Description = "Delivery options for the country found and returned successfully")]
[SwaggerResponseExample(tHttpStatusCode.OK, typeof(DeliveryOptionsModelExample))]
[SwaggerResponse(HttpStatusCode.BadRequest, Type = typeof(ErrorsModel), Description = "An invalid or missing input parameter will result in a bad request")]
[SwaggerResponse(HttpStatusCode.InternalServerError, Type = typeof(ErrorsModel), Description = "An unexpected error occurred, should not return sensitive information")]
public async Task<IHttpActionResult> DeliveryOptionsForAddress(DeliveryOptionsSearchModel search)
{

Now implement it, in this case via a DeliveryOptionsSearchModelExample (which should implement IExamplesProvider), which will generate the example data. It should return the type you specified when you specified the [SwaggerRequestExample].

public class DeliveryOptionsSearchModelExample : IExamplesProvider
{
    public object GetExamples()
    {
        return new DeliveryOptionsSearchModel
        {
            Lang = "en-GB",
            Currency = "GBP",
            Address = new AddressModel
            {
                Address1 = "1 Gwalior Road",
                Locality = "London",
                Country = "GB",
                PostalCode = "SW15 1NP"
            },
            Items = new[]
            {
                new ItemModel
                {
                    ItemId = "ABCD",
                    ItemType = ItemType.Product,
                    Price = 20,
                    Quantity = 1,
                    RestrictedCountries = new[] { "US" }
                }
            }
        };
    }

Don’t forget to enable the ExamplesOperationFilter when you enable Swagger, as before:

configuration
    .EnableSwagger(c =>
    {
        c.OperationFilter<ExamplesOperationFilter>();
    })
    .EnableSwaggerUi();

Now that we’ve done all that, we should see the examples output in our swagger.json file, which you can get to by starting your solution and navigating to /swagger/docs/v1.

Capture

And the best part is, when you’re using swagger-ui (at /swagger/ui/index), now when you click the example request in order to populate the form, instead of getting an autogenerated request like this:

Untitled

You’ll get your desired example, like this:

Capture2

I find that having a valid request on hand is useful for smoke testing your API endpoints are working correctly.

Advertisements

36 thoughts on “Generating Swagger example requests with Swashbuckle

  1. line 38 of SetResponseModelExamples
    should be
    var responseAttributes = apiDescription.GetControllerAndActionAttributes();

    of you could rename SwaggerRequestExamplesAttribute class to SwaggerResponseExamplesAttribute, which is what I did when implementing your sample

    thanks again.

  2. Great article! And of course, a question:

    I have an API with a polymorphic service endpoint: /api/v1/vehicles. The endpoint accepts a parameter of type Vehicle, which is an abstract class with shared vehicle properties and a VehicleType enumeration property (enumeration values of Car, Truck, ..). Internally, the API uses a JsonConverter class to resolve the concrete Vehicle based on the VehicleType property that is passed in.

    The Swashbuckle documentation shows the Vehicle schema as my input parameter, but I would like to replace it with examples of the various concrete Vehicles that can be passed in. Using the pattern you describe in this post produces a run-time javascript/swagger exception. I assume it’s because the abstract Vehicle and concrete classes have different schemas. Any idea on whether it is possible to do what I want to do, or if there is a better solution for my problem?

    Thanks in advance.

    • I wouldn’t expect that to be an issue – I’m not the author of SwaggerResponse but I would think you should be able to specify the typeof one of your concrete classes as the Type parameter and it would be happy. Did you try that?

      Maybe ask your question in the Swashbuckle github page.

      • Matt, thanks for your response. Using typeof of my concrete class in the attribute declaration does not work because my concrete class does not implement IExampleProvider. The issue is in the schema replacement. As far as support from swashbuckle, the package doesn’t offer official support for documenting polymorphic endpoints (https://github.com/domaindrivendev/Swashbuckle/issues/313), so I may have to change my implementation.

  3. Thanks, Are you able to update example to latest version (6.0.0-beta902) it looks like IOperationFilter has changed since this article was posted.

    Thanks again

      • Hi Matt,

        Could you please help me out in implementing default request data to the controller action of the model schema on the Swagger UI with Swagger 6.0.0-rc1-final with C#, same like your above implementation. As i’m using Swagger 6.0.0-rc1-final the functions which are used at above link are not found in this swagger which i’m using.

      • Sorry Kumar, but I haven’t looked at Swashbuckle 6 – my work project is on Swashbuckle 5 and it works, so I’m not likely to upgrade it if there’s breaking changes. If you need support you could try posting an issue on the Swashbuckle github. Alas I don’t have any spare time to play around with this outside of work.

  4. Hi Matt,

    Thank you for the example, very useful.

    For me to get it working I used Newtonsoft.Json (otherwise the Swagger UI show nothing)

    Newtonsoft.Json.JsonConvert.SerializeObject(provider.GetExamples(), Formatting.Indented);
    // ((dynamic)FormatAsJson(provider))[“application/json”];

    and also had to comment out some parts of the following

    var response =
    operation.responses.FirstOrDefault(
    x => x.Value != null); // && x.Value.schema.@ref == schema.@ref && x.Value.schema != null); //

    Any idea what I might be missing?

    Thanks,
    Gavin

    Full code below.

    private static void SetRequestModelExamples(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
    var requestAttributes = apiDescription.GetControllerAndActionAttributes();

    foreach (var attr in requestAttributes)
    {
    var schema = schemaRegistry.GetOrRegister(attr.ResponseType);

    var request = operation.parameters.FirstOrDefault(p => p.@in == “body” && p.schema.@ref == schema.@ref);

    if (request != null)
    {
    var provider = (IProvideExamples)Activator.CreateInstance(attr.ExamplesType);

    var parts = schema.@ref.Split(‘/’);
    var name = parts.Last();

    var definitionToUpdate = schemaRegistry.Definitions[name];

    if (definitionToUpdate != null)
    {
    definitionToUpdate.example = Newtonsoft.Json.JsonConvert.SerializeObject(provider.GetExamples(), Formatting.Indented);
    // ((dynamic)FormatAsJson(provider))[“application/json”];

    }
    }
    }
    }
    private static void SetResponseModelExamples(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
    var responseAttributes = apiDescription.GetControllerAndActionAttributes();

    foreach (var attr in responseAttributes)
    {
    var schema = schemaRegistry.GetOrRegister(attr.ResponseType);

    var response =
    operation.responses.FirstOrDefault(
    x => x.Value != null); // && x.Value.schema.@ref == schema.@ref && x.Value.schema != null); //

    if (response.Equals(default(KeyValuePair)) == false)
    {
    if (response.Value != null)
    {
    var provider = (IProvideExamples)Activator.CreateInstance(attr.ExamplesType);
    response.Value.examples = FormatAsJson(provider);
    }
    }
    }
    }

    • I think if you had to comment out that line to get it to work, then you haven’t defined the [SwaggerRequestExamples] attribute above your controller’s method, like I do above:

      [SwaggerRequestExamples(typeof(DeliveryOptionsSearchModel), typeof(DeliveryOptionsSearchModelExample))]

  5. Excelent post! Very useful.
    Just one question about the displayed screen capture. How do you get the Authorization (access token) input field?
    Is it some customization?

    Thanks!

  6. Is it possible to manage multiple default values when the endpoint request model has more than one parameter?

    i.e.:
    public async Task GetDocument (RequestBase request, int Id)

    Can I post default values for request object, but also for Id ?

    How can I handle this…?

    Thanks

    • C# allows you to pass default parameters at compile time so you should be able to do:
      public async Task GetDocument (RequestBase request, int id = 3)

      To set defaults on your RequestBase object, you could do it a number of ways. The easiest place is on the RequestBase’s constructor.

      Another way would be to have a static “default” RequestBase object and set it to that in your controller action method. e.g.
      public async Task GetDocument (RequestBase request = null, int id = 3)
      {
      request = request ?? DefaultRequestBase;
      }

      Another way would be to take advantage of ASP.NET’s model binding and explicitly pass through the RequestBase’s properties to your controller action and default them there e.g. assuming your RequestBase looks something like this:
      public class RequestBase
      {
      public int Blah { get; set; }
      }

      public async Task GetDocument (int blah = 7, int id = 3)

      My favourite approach is another way – have a specific GetDocument request object which inherits from RequestBase and use that in your action method. Then have the default values in the GetDocumentRequest constructor.

      public async Task GetDocument (GetDocumentRequest documentRequest)

      • Thanks! I think I didn’t explain myself correctly.

        What I need to know if it’s possible is how can I define a the decorator SwaggerRequestExamples when I have more than one parameter in my endpoint.

        i.e.:
        [SwaggerRequestExamples(typeof(RequestBase), typeof(RequestBaseExamples))]
        public async Task GetDocument (RequestBase request, int Id)

        In this case, I can implement the GetExamples method for RequestBase class, but I can’t define the example value for Id parameter.

        Did I explain my self this time?

        Thanks again!

      • Oh, OK. You should be able to define the attribute multiple times:

        [SwaggerRequestExamples(typeof(RequestBase), typeof(RequestBaseExamples))]
        [SwaggerRequestExamples(typeof(int), typeof(IntExample))]
        public async Task GetDocument (RequestBase request, int Id)

        But I think it would be better to create a specific GetDocumentRequest object which inherits your RequestBase.
        [SwaggerRequestExamples(typeof(GetDocumentRequest), typeof(GetDocumentRequestExample))]
        public async Task GetDocument (GetDocumentRequest request)

      • Thanks mattfrear!
        I tried with the first approach. But it doesn’t work. It says:
        Duplicate ‘SwaggerRequestExamples’ attribute. I think it’s not possible.

        The thing with your 2nd approach, is that my endpoint has some body parameters (RequestBase) and some uri parameters (Id). That’s why I don’t have all the endpoint parameters encapsulated in a single class inheriting RequestBase.

        Do you think is it possible to decorate my endpoint in such a way it would be possible to define default values for my request parameters?

        Thanks again!

      • Nope – you can’t define default values for a query string request parameter.

        My ExamplesOperationFilter populates the “example” property of a schema object in the swagger json.

        Thus in order to have an “example” a parameter must have a “schema” property.

        The swagger spec at http://swagger.io/specification/#parameterObject states that the schema property can only be set if the “in” of a parameter is “body” (i.e. not “query”).

        Thus what you’re trying to do can’t be done, according to the swagger spec

  7. Hi Matt!
    Thanks for the answer.
    Do you know alternative where I can store comments for Response messages? Is it ok to store this in implementation notes ? How developers of Swash solve this problem of commenting ?
    Vitaly

    • I just did a bit of digging through the swagger spec, and what you need to do is supported, by adding a “description” property to the definition. I couldn’t actually see this in the swagger spec but if you look at the swagger petstore example (http://editor.swagger.io/) and search for “pet status in the store” you will see it in the example .yaml. You can use the File menu in that editor to download the swagger.json and then see the descripton there.

      I haven’t got time right now to implement this but it should be a simple modification to my ExamplesOperationFilter to add the description property. You could probably set a [Description] attribute on each property and read that in the ExamplesOperationFilter.

      I’ll have a look at doing this for you in the next week. No guarantees though – as I say it should be simple to implement if you want to do it.

      • Vitaly – I have implemented this for you.

        1. Pull down the latest version of the NuGet package
        2. Register the new DescriptionOperationFilter – c.OperationFilter();
        3. Add a [Description(“this is a description”)] attribute to your response model

        Then in the swagger.json, you should see the description field populated, e.g:
        “PersonResponse”: {
        “type”: “object”,
        “properties”: {
        “id”: {
        “format”: “int32”,
        “type”: “integer”
        },
        “firstName”: {
        “description”: “The first name of the person”,
        “type”: “string”
        },

  8. I had a similar issue to what was noted earlier. There was no swagger UI unless I changed the code slightly for the request example:

    if (definitionToUpdate != null)
    {
    //definitionToUpdate.Example = ((dynamic)FormatAsJson(provider))[“application/json”];
    definitionToUpdate.Example = Newtonsoft.Json.JsonConvert.SerializeObject(provider.GetExamples(), Formatting.Indented, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
    }

    The difference is with dynamic, the example in swagger.json is an object:
    “example”: {
    “companyId”: “6090511f-50d9-44b0-a657-f2c4a697f6ff”,
    “name”: “ABC Co.”,
    “internalId”: null
    }

    And with the JsonConvert the result is a string:
    “example”: “{\r\n \”CompanyId\”: \”6090511f-50d9-44b0-a657-f2c4a697f6ff\”,\r\n \”Name\”: \”ABC Co.\”,\r\n \”InternalId\”: null\r\n}”

    I assume the correct result is a string to display in the box in the UI?. I’m using the ASP.NET core library.

    Thank you

    • Hi hubbins,

      Thanks for your notes. The swagger spec is loose as to what an “example” can be:
      http://swagger.io/specification/#schemaObject
      It says: “example Any A free-form property to include an example of an instance for this schema.”

      So it appears we are both correct.

      I implemented it so that an example is a JSON object, and that works fine for me – as per the screenshot in my blog post, swagger-ui “Model Schema” box is populated, and when clicked the example request gets copied into the request parameter.

      I’m interested to know why it’s not working for you. If you have time, could you create an issue on the GitHub page (ideally with some repro steps).

      Thanks

      • The issue was returning a null for a value in the request sample, this would result in a client-side parsing error. In my sample request, there are several optional values, instead of rendering them with a value of null, now they are omitted. It would be better to show them with null, but I think there’s some disagreement about that in the spec.

        “Uncaught TypeError: Swagger 2.0 does not support null types ([object Object]). See https://github.com/swagger-api/swagger-spec/issues/229

        I added “Ignore” for null value handling here and it resolved the issue. I’m not sure this is correct with regard to the spec, though.

        private static object ConvertToCamelCase(Dictionary examples)
        {
        var jsonString = JsonConvert.SerializeObject(examples, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver(), NullValueHandling = NullValueHandling.Ignore });
        return JsonConvert.DeserializeObject(jsonString);
        }

      • Thanks for the reply. I have had a play, and have implemented your NullValueHandling fix for the request examples. It seems to work fine with the (dynamic) casting so I have left that in instead of returning the example as a string.

        The code is updated in GitHub, and I have published a new version of the NuGet packages.

        Have a play, and let me know if you have any problems.

        Thanks

  9. I use the SwaggerRequestExampleAttribute with a List. If I try to load the swagger page I get an NullReferenceException. If I use a None List-Class it’s works.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s