Adding Query String Params to my Swagger Specs
Asked Answered
O

4

19

I am using Swashbuckle (swagger for C#) with my Web API. I have several GET End-Points that return lists and I allow the user to add a perpage and page params into the QueryString

Example: http://myapi.com/endpoint/?page=5&perpage=10

I see that swagger does support parameter in 'query' but how do I get Swashbuckle to do it?


I mention in one of the comments that I solved my issue by creating a custom attribute to allow me to do what I needed. Below is the code for my solution:

[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
public class SwaggerParameterAttribute : Attribute
{
    public SwaggerParameterAttribute(string name, string description)
    {
        Name = name;
        Description = description;
    }

    public string Name { get; private set; }
    public Type DataType { get; set; }
    public string ParameterType { get; set; }
    public string Description { get; private set; }
    public bool Required { get; set; } = false;
}

Register the Attribute with the Swagger Config:

GlobalConfiguration.Configuration 
    .EnableSwagger(c =>
        {
            c.OperationFilter<SwaggerParametersAttributeHandler>();
        });

Then add this attribute to your methods:

[SwaggerParameter("page", "Page number to display", DataType = typeof(Int32), ParameterType = ParameterType.inQuery)]
[SwaggerParameter("perpage","Items to display per page", DataType = typeof(Int32), ParameterType = ParameterType.inQuery)]
Ovine answered 6/3, 2016 at 14:52 Comment(2)
Where does SwaggerParametersAttributeHandler come from? :sCharters
Darn, apparently the ParameterType enum is also missing. Any chance you'd be willing to fill in the blanks for us? :DCharters
M
13

You can achieve that quite easily. Suppose you have an ItemsController with an action like this:

[Route("/api/items/{id}")]
public IHttpActionResult Get(int id, int? page = null, int? perpage = null)
{
   // some relevant code
   return Ok();
}

Swashbuckle will generate this specification (only showing relevant part):

"paths":{  
  "/api/items/{id}":{  
     "get":{  
        "parameters":[  
           {  
              "name":"id",
              "in":"path",
              "required":true,
              "type":"integer",
              "format":"int32"
           },
           {  
              "name":"page",
              "in":"query",
              "required":false,
              "type":"integer",
              "format":"int32"
           },
           {  
              "name":"limit",
              "in":"query",
              "required":false,
              "type":"integer",
              "format":"int32"
           }
        ]
     }
  }

When you want page and perpage to be required, just make the parameters not nullable.

Marrilee answered 8/3, 2016 at 21:12 Comment(8)
That is a very reasonable answer but I ended up creating a custom Swagger Attribute to handle itOvine
How did you achieve that @JasonH? Are sources online that can point to a solution like you have mentioned?Pickled
The code I was working on is not owned by myself so I did not share the attribute I created. I can edit my original question with what I did so you can see.Ovine
Thanks @JasonH. Is something missing from the example you have given? How does swagger know how to parse this attribute you have created?Pickled
Sorry, you are right, I forgot an important part. You have to register the attribute with the Swagger Configuration. I have edited my original post to show this.Ovine
Thanks @JasonH! And this SwaggerParametersAttributeHandler class is somehow creating query params on the docs for each attribute on the method?Pickled
@Kazuo, I will be honest this thread is almost a year old. I have not worked with Swagger since this project. I am due to start a new project tomorrow where I will be using swagger and autoREST on the API. If I recall, it does add the information to the JSON data for the query string definitions you put into the attribute. I cannot recall if that shows up in the SwaggerUI or not. Your best bet is to implement it and play with it.Ovine
@JasonH I got it figured it. The clue was the operation filter thanks!. I have it auto adding query param text boxes to my docs now by decorating the controller methods. Ill post my solution as an answer to your question later so that other people can benefit from this solution. Thanks again!Pickled
C
6

Here is a summary of the steps required (ASP.Net Core 2.1, Swashbuckle.AspNetCore v4.0.1) for the Attribute method. I needed a parameter starting with "$" so optional parameters were not an option!

SwaggerParameterAttribute.cs

     [AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
     public class SwaggerParameterAttribute : Attribute
     {
         public SwaggerParameterAttribute(string name, string description)
        {
            Name = name;
            Description = description;
        }

        public string Name { get; private set; }
        public string DataType { get; set; }
        public string ParameterType { get; set; }
        public string Description { get; private set; }
        public bool Required { get; set; } = false;
    }

SwaggerParameterAttributeFilter.cs

using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
using System.Linq;
public class SwaggerParameterAttributeFilter : IOperationFilter
{
    public void Apply(Operation operation, OperationFilterContext context)
    {
        var attributes = context.MethodInfo.DeclaringType.GetCustomAttributes(true)
            .Union(context.MethodInfo.GetCustomAttributes(true))
            .OfType<SwaggerParameterAttribute>();

        foreach (var attribute in attributes)
            operation.Parameters.Add(new NonBodyParameter
            {
                Name = attribute.Name,
                Description = attribute.Description,
                In = attribute.ParameterType,
                Required = attribute.Required,
                Type = attribute.DataType
            });              
    }
}

add this in Startup.ConfigureServices

 using Swashbuckle.AspNetCore.Swagger;
 services.AddSwaggerGen(c =>
 {
      c.OperationFilter<SwaggerParameterAttributeFilter>();
      c.SwaggerDoc("v1.0", new Info { Title = "My API", Version = "v1.0" });
 });

and use like this:

[SwaggerParameter("$top", "Odata Top parameter", DataType = "integer", ParameterType ="query")]

DataTypes can be: integer, string, boolean

ParameterTypes: can be path, body, query

Cordeiro answered 15/11, 2018 at 15:38 Comment(0)
R
2

I know this is old and all but I spent some time looking for the easier method and found it. So for anyone who also stumbles onto this old thread, here it is:

public async Task<IActionResult> Foo([FromQuery]YourDtoType dto)
Ramtil answered 1/10, 2022 at 0:10 Comment(1)
This is exactly what OP is looking for, needs to be top answerMonamonachal
P
0

There's some comments here regarding missing information on the SwaggerParametersAttributeHandler. It is an operation filter to help you determine what to do to your attributes.

Here's an example handler I used that allows me to override nullable parameters' required fields using the SwaggerParameterAttribute.

public class RequiredParameterOverrideOperationFilter : IOperationFilter
{
    public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
    {
        // Get all SwaggerParameterAttributes on the method
        var attributes = apiDescription.ActionDescriptor.GetCustomAttributes<SwaggerParameterAttribute>();

        if (operation.parameters == null)
        {
            operation.parameters = new List<Parameter>();
        }

        // For each attribute found, find the operation parameter (this is where Swagger looks to generate the Swagger doc)
        // Override the required fields based on the attribute's required field
        foreach (var attribute in attributes)
        {
            var referencingOperationParameter = operation.parameters.FirstOrDefault(p => p.name == attribute.Name);

            if (referencingOperationParameter != null)
            {
                referencingOperationParameter.required = attribute.Required;
            }
        }
    }
}
Playground answered 8/12, 2017 at 16:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.