1

I have a simple web API2 project that uses swagger for it's documentations.

Given a simple GET endpoint that uses route parameters and query parameters such as:

[HttpGet]
[Route("api/v2/items/{itemid:int}")]
public IHttpActionResult Getv2(int itemId, [FromUri(Name ="")]DTOv1 request)
{
    return Ok();
}

public class DTOv1
{
    public DateTime? StartValue { get; set; }
}

This gives the following documentation: Correct Swagger documentation

However, I would like to be able to specify all the items in a POCO. Such as:

[HttpGet]
[Route("api/v3/items/{itemid:int}")]
public IHttpActionResult Getv3([FromUri(Name ="")]DTOv2 request)
{
    return Ok();
} 

public class DTOv2
{
    public int ItemId { get; set; }
    public DateTime? StartValue { get; set; }
}

This gives the following Incorrect documentation: Incorrect Swagger documentation

This GET endpoint works in the same way as the first example but as you can see the documentation does not, and trying to do an example will not work. Is it possible to configure swagger so that this is documented in the same way as the first example, ideally in a convention based way?

Swagger is just using the default setup:

GlobalConfiguration.Configuration
    .EnableSwagger(c =>
        {
            c.SingleApiVersion("v1", "TestSwagger");
            c.PrettyPrint();
        })
    .EnableSwaggerUi(c =>
        {
        });

EDIT:

Thanks to the response regarding adding filters by I wrote the following operation filter that works in our use case to manipulate the parameters:

private class OperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        if (apiDescription.HttpMethod.Method == "GET")
        {
            var pathParams = operation.parameters.Where(x => x.@in == "path");

            var toRemoveItems = new List<Parameter>();
            foreach(var pathParam in pathParams)
            {
                toRemoveItems.AddRange(operation
                    .parameters
                    .Where(x => x.@in != "path" && x.name.EndsWith(pathParam.name)));                     
            }

            foreach(var toRemove in toRemoveItems)
            {
                operation.parameters.Remove(toRemove);
            }
        }
    }
}
4
  • If you want to specify all the items in a POCO, they should not be on the route... Commented Jan 16, 2018 at 13:40
  • i could see that being true , but what would be the reasoning to that. other than it not playing nice with the document generation, the endpoint does work corectly. Commented Jan 16, 2018 at 14:01
  • If you want to bend the rules you could use a document filter (IDocumentFilter), that will give you 100% control over the final swagger.json, but be aware you could endup with something that in not compliant with OAS 2.0 Commented Jan 16, 2018 at 16:59
  • One thing. The route attribute is case sensitive looked up against the object name. so [Route("api/v3/items/{itemid:int}")] should be [Route("api/v3/items/{ItemId}")]. This at least sets the parameter as a path parameter Commented Jan 16, 2018 at 23:28

1 Answer 1

1

Following up with my suggestion from the comments about using an IDocumentFilter here is a starting point:

    private class RouteTestDocumentFilter : IDocumentFilter
    {
        const string PATH = "/api/RouteTest/test/{itemid}";

        public void Apply(SwaggerDocument swaggerDoc, SchemaRegistry s, IApiExplorer a)
        {
            if (swaggerDoc.paths != null && swaggerDoc.paths.ContainsKey(PATH))
            {
                var get = swaggerDoc.paths[PATH].get;
                if (get != null)
                {
                    get.parameters.RemoveAt(0);
                    get.parameters[0].@in = "path";
                    get.parameters[0].required = true;

                    foreach (var param in get.parameters)
                    {
                        int pos = param.name.IndexOf('.');
                        if (pos > 0)
                            param.name = param.name.Substring(pos + 1);
                    }
                }
            }
        }
    }

For more details see my commit:
https://github.com/heldersepu/SwashbuckleTest/commit/38a31e0ee700faf91cc38d005ae1c5f4bec3e1f3

Here is how it looks on the UI:
http://swashbuckletest.azurewebsites.net/swagger/ui/index?filter=RouteTest#/RouteTest/RouteTest_Get

Sign up to request clarification or add additional context in comments.

2 Comments

Thanks to this post, I looked at using an operation filter for GET requests, finding all the path parameters and then removing query parameters with the same name. This seems to do the trick and is generic enough for my use case across different end points.
I still feel that this is a workaround in a limitation of the swashbuckle generation. I will post the code I wrote for anyone who is interested

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.