26

How do I define default value for property in swagger generated from following API?

public class SearchQuery
{
        public string OrderBy { get; set; }

        [DefaultValue(OrderDirection.Descending)]
        public OrderDirection OrderDirection { get; set; } = OrderDirection.Descending;
}


public IActionResult SearchPendingCases(SearchQuery queryInput);

Swashbuckle generates OrderDirection as required parameter. I would like it to be optional and indicate to client the default value (not sure if swagger supports this).

I don't like making the property type nullable. Is there any other option? Ideally using built in classes...

I use Swashbuckle.AspNetCore - https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio

Liero
  • 25,216
  • 29
  • 151
  • 297

5 Answers5

20

I've always set the default on the param itself like this:

public class TestPostController : ApiController
{
    public decimal Get(decimal x = 989898989898989898, decimal y = 1)
    {
        return x * y;
    }
}

Here is how that looks like on the swagger-ui:
http://swashbuckletest.azurewebsites.net/swagger/ui/index#/TestPost/TestPost_Get


UPDATE

Following the discussion on the comments I updated Swagger-Net to read the DefaultValueAttribute via reflection Here is the sample class I'm using:

public class MyTest
{
    [MaxLength(250)]
    [DefaultValue("HelloWorld")]
    public string Name { get; set; }
    public bool IsPassing { get; set; }
}

and here is how the swagger json looks like:

"MyTest": {
  "type": "object",
  "properties": {
    "Name": {
      "default": "HelloWorld",
      "maxLength": 250,
      "type": "string"
    },
    "IsPassing": {
      "type": "boolean"
    }
  },
  "xml": {
    "name": "MyTest"
  }
},

The Source code of Swagger-Net is here:
https://github.com/heldersepu/Swagger-Net

And the source code for the test project is here:
https://github.com/heldersepu/SwashbuckleTest

Community
  • 1
  • 1
Helder Sepulveda
  • 15,500
  • 4
  • 29
  • 56
  • I know but I have a reason for class. If this works, the parameters defined by class should work as well – Liero Jan 12 '18 at 16:15
  • @Liero The best explanation I can give you is that for a default value you need something that we can get at compile time, but your assignment does not happen until someone instantiates the SearchQuery class – Helder Sepulveda Jan 14 '18 at 11:12
  • Of course, that's why annotation attributes (which can be retrieved via reflection) are there. Maybe swashbuckle does support DefaultValueAttribute (or simmilar solution) out of the box, but I'm sure the functionality can be added via configuration – Liero Jan 14 '18 at 12:57
  • Ahhh, I see! ... But on Swashbuckle I do not see they support that: https://github.com/domaindrivendev/Swashbuckle/search?utf8=%E2%9C%93&q=DefaultValueAttribute – Helder Sepulveda Jan 14 '18 at 13:16
  • 1
    But @Liero fear not! I have my own fork of Swashbuckle let's see how long it will take me to do that... – Helder Sepulveda Jan 14 '18 at 13:19
  • @Liero Of course, everything is on GitHub: https://github.com/heldersepu – Helder Sepulveda Jan 15 '18 at 12:20
  • 3
    Unfortunatelly, it is not asp.net core – Liero Jan 15 '18 at 12:41
7

Setting the default parameter value works like this if you can do it in your controller

// GET api/products
[HttpGet]
public IEnumerable<Product> Get(int count = 50)
{
    Conn mySqlGet = new Conn(_connstring);
    return mySqlGet.ProductList(count);
}
Colbs
  • 587
  • 10
  • 25
3

This is applicable for ASP.net MVC5, code is not valid for .Net Core

1- Define a custom attribute as following

public class SwaggerDefaultValueAttribute: Attribute
{
   public SwaggerDefaultValueAttribute(string param, string value)
   {
      Parameter = param;
      Value = value;
   }
   public string Parameter {get; set;}
   public string Value {get; set;}
}

2- Define a Swagger OperationFilter class

public class AddDefaulValue: IOperationFilter
{
   void IOperationFilter.Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
   {
      if (operation.parameters == null || !operation.parameters.Any())
      {
         return;
      }
      var attributes = apiDescription.GetControllerAndActionAttributes<SwaggerDefaultValueAttribute>().ToList();

      if (!attributes.Any())
      {
         return;
      }
      
      foreach(var parameter in operation.parameters)
      {
         var attr = attributes.FirstOrDefault(it => it.Parameter == parameter.name);
         if(attr != null)
         {
            parameter.@default = attr.Value;
         }
      }
   } 
}

3- Register the OperationFilter at SwaggerConfig file

c.OperationFilter<AddDefaultValue>();

4- Decorate your controller method with attributes

[SwaggerDefaultValue("param1", "AnyValue")]
public HttpResponseMessage DoSomething(string param1)
{
   return Request.CreateResponse(HttpStatusCode.OK);
}
Sameh
  • 1,318
  • 11
  • 11
  • Nice, but can u tell me how we can use this for mutiple params? Edit: add [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] to the attribute class. – Yan Sep 20 '21 at 12:14
0

First approach

According to one of the answers here you should be able to simply add the following to your model although I haven't verified this:

 public class ExternalJobCreateViewModel
{
    ///<example>Bob</example>
    public string CustomFilename { get; set; }
    ...etc

Second approach

In .net 6 I used the following:

public class MyFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {

        if (operation.OperationId.Equals("somecontroller_somepath", StringComparison.OrdinalIgnoreCase))
        {                
            operation.Parameters.Clear();
            operation.Parameters = new List<OpenApiParameter>
            {
                new OpenApiParameter()
                {
                    Name = "document-name",
                    Schema = new OpenApiSchema()
                    {
                        Type = "string",
                    },
                    Example = new Microsoft.OpenApi.Any.OpenApiString("docName1"),
                    In = ParameterLocation.Query
                },
                new OpenApiParameter()
                {
                    Name = "user-email",
                    Schema = new OpenApiSchema()
                    {
                        Type = "string",
                    },
                    Example = new Microsoft.OpenApi.Any.OpenApiString("elvis.presley@somemail.com"),
                    In = ParameterLocation.Query
                },
                new OpenApiParameter()
                {
                    Name = "account-type-id",
                    Schema = new OpenApiSchema()
                    {
                        Type = "string",
                    },
                    Example = new Microsoft.OpenApi.Any.OpenApiString("2"),
                    In = ParameterLocation.Query
                }
            };
        }
    }
}

Then in Program.cs

builder.Services.AddSwaggerGen(options =>
            {
                ... other stuf
                options.OperationFilter<MyFilter>();

3rd Approach

I never used this code so not tested

In .net 6 and Piggybacking off of Sameh's answer...

To use multiple attributes decorate your attribute class like this:

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerDefaultValueAttribute : Attribute
{
 ... etc

For 6.5.0 of swashbuckle I think it is something like this for properties:

public class AddDefaulValueFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null || !operation.Parameters.Any())
        {
            return;
        }
        context.ApiDescription.TryGetMethodInfo(out var x);
        if (x != null)
        {
            return;
        }
        var attributes = x!.GetCustomAttributes<SwaggerDefaultValueAttribute>().ToList();

        if (!attributes.Any())
        {
            return;
        }

        foreach (var parameter in operation.Parameters)
        {
            var attr = attributes.FirstOrDefault(it => it.Parameter == parameter.Name);
            if (attr != null)
            {
                parameter.Schema.Default = new OpenApiString(attr.Value);
            }
        }
    }
}

Since I was trying to use this for body parameters on a multipart message I Had to do something like this but use at your own risk:

public class AddDefaulValueFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if(operation.RequestBody == null)
        {
            return;
        }
        var keys = operation.RequestBody.Content.Where(val => val.Key == "multipart/form-data").Take(1).ToList();
        if(!keys.Any())
        {
            return;
        }
        var props = keys.FirstOrDefault().Value.Schema.Properties;
        if (props == null || !props.Any())
        {
            return;
        }
        context.ApiDescription.TryGetMethodInfo(out var x);
        if (x == null)
        {
            return;
        }
        var attributes = x!.GetCustomAttributes<SwaggerDefaultValueAttribute>().ToList();

        if (!attributes.Any())
        {
            return;
        }

        foreach (var prop in props)
        {
            var attr = attributes.FirstOrDefault(it => it.Parameter == prop.Key);
            if (attr != null)
            {
                prop.Value.Default = new OpenApiString(attr.Value);
            }
        }
    }
}
Post Impatica
  • 14,999
  • 9
  • 67
  • 78
-3

In the YAML file, you can define which properties should be required. This example is from a NSwag configuration.

/SearchPendingCases:
    post:
      summary: Search pending cases
      description: Searches for pending cases and orders them
      parameters:
        - in: body
          name: SearchQuery 
          required: true
          schema:
            type: object
            required:
              - OrderBy
              # do not include OrderDirection here because it is optional
            properties:
              OrderBy:
                description: Name of property for ordering
                type: string
                # you can remove the following two lines 
                # if you do not want to check the string length
                minLength: 1    
                maxLength: 100
              OrderDirection:
                description: Optional order direction, default value: Descending
                type: string
                enum: [Ascending, Descending] # valid enum values
                default: Descending           # default value

Swagger - Enums

Swagger - Unlocking the Spec: The default keyword

Georg Patscheider
  • 9,357
  • 1
  • 26
  • 36
  • 1
    Maybe I should state it explicitly, but I actually have swagger documentation generated by Swashbuckle.AspNetCore library, which is recommended by official [asp.net core docs](https://learn.microsoft.com/en-us/aspnet/core/tutorials/web-api-help-pages-using-swagger?tabs=visual-studio) – Liero Jan 12 '18 at 10:28
  • Sorry, I am not familiar with this library :( – Georg Patscheider Jan 12 '18 at 10:41