Hi, First of all, brilliant work. I'm new to Swagger and Swashbuckle made life very easy for us to integrate into our API, a few well written comments later and we're good.
We are facing one minor issue in which our models assume default values which don't look that appealing. See the image attached.

Is there a way to specify a default value for model properties? I have had no luck using default value attributes so far e.g.
[System.ComponentModel.DefaultValue("My Default String Value")]
public string Value { get; set; }
Hey there
Let me start by saying, there's currently no simple way to do this. But, I have some ideas for a potential (non-perfect) workaround. Read on if your interested in trying these out ...
Sample values are "semi" supported by the Swagger Spec and subsequently through the swagger-ui. For a given "Parameter", you can specify a defaultValue. In the case of primitive types, this value will be automatically populated in the corresponding input field or drop-down.
You can also specify a defaultValue for complex type parameters. The provided value must be a JSON string and when included with the Swagger API description, the corresponding textarea will be populated with that exact string. Unfortunately though, the not-so-appealing values will still be present in the "Model Schema" displayed to the right of the UI. Maybe this is good enough?
So, to set this "defaultValue" property for specific complex parameters, you would use Swashbuckle's "Operation Filter" abstraction (see the readme) and hardcode default JSON values according to the parameter Name or Type.
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, DataTypeRegistry dataTypeRegistry, ApiDescription apiDescription)
{
foreach (var param in operation.Parameters)
{
switch(param.Name)
{
case "bet":
param.DefaultValue = "{ \"events\": [ \"event_id\": \"foobar\" ] }";
// etc. ...
Rather than hardcoding, you could even go a step further and pull the corresponding Type out of the ApiDescription (apiDescription.ParameterDescriptions ... ParameterType ...). Then, you could use this to generate a sample JSON string, potentially picking up default values from property attributes.
Sounds like a lot of work but would be a more sustainable solution going forward - maybe even a feature that could ultimately be added to Swashbuckle.
The other option, which I haven't really delved too deeply, is to inject some custom JavaScript that hooks into the swagger-ui and applies the default values accordingly. This would involve a little more investigation, specifically into the swagger-ui code but may end-up being easier than the above workaround. See the Swashbuckle readme for instructions around injecting custom JavaScript.
Cheers
Richie
Thanks for the pointers, I will have a play with the operation filters and let you know how I get on.
Doh - just realized the DefaultValue property isn't defined in the latest Swashbuckle release (4.0.0). For some reason, it was omitted from the Swagger object model so you won't be able to set it in a filter. It's since been added to the master branch, which I plan to release very shortly, so you'll have to clone and build locally to try this out.
Ok, thanks for the heads up.
I've just pushed a pre-release version (4.1.0-rc1) that now includes the DefaultValue property.
Install-Package Swashbuckle -Pre
Let me know how it goes
Thanks
Richie
I get no definition for 'DefaultValue'
I am definetely running rc1 does the core library need updating too? My packages.config after Install-Package looks like
<package id="Swashbuckle" version="4.1.0-rc1" targetFramework="net45" />
<package id="Swashbuckle.Core" version="4.0" targetFramework="net45" />
Looks like I forgot to update the dependency version ... yep you'll need to update the Core package manually.
Great this works really well. I have a custom attribute which defines parameter defaults, this is then picked up by the IOperationFilter. Example below.
public class SwaggerDefaultValue : Attribute
{
public string ParameterName { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string parameterName, string value)
{
this.ParameterName = parameterName;
this.Value = value;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, DataTypeRegistry dataTypeRegistry, ApiDescription apiDescription)
{
foreach (var param in operation.Parameters)
{
var actionParam = apiDescription.ActionDescriptor.GetParameters().First(p => p.ParameterName == param.Name);
if (actionParam != null)
{
var customAttribute = actionParam.ActionDescriptor.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault();
if (customAttribute != null)
{
param.DefaultValue = customAttribute.Value;
}
}
}
}
}
Then in my controller I define the parameters as so...
[HttpGet]
[SwaggerDefaultValue("id", "101")]
[SwaggerDefaultValue("search", "foobar")]
public SomeObject Get(int id, string search)
{
...
}
Seems to work really well. You mention there is no support for injecting values into the model schema, is that a hard and fast requirement from swagger ui or that you don't see a way to implement it via swashbuckle? Great work though.
Going to close this issue for now but I plan to release a version supporting Swagger 2.0, which has much better support for documentation/samples etc.. I've tentatively committed to a release date in early Nov.
for 6.0
[ AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerDefaultValue : Attribute
{
public string ParameterName { get; set; }
public string Value { get; set; }
public SwaggerDefaultValue(string parameterName, string value)
{
this.ParameterName = parameterName;
this.Value = value;
}
}
public class AddDefaultValues : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
return;
foreach (var param in operation.parameters)
{
var actionParam = apiDescription.ActionDescriptor.GetParameters().First(p => p.ParameterName == param.name);
if (actionParam != null)
{
var customAttribute = actionParam.ActionDescriptor.GetCustomAttributes<SwaggerDefaultValue>().FirstOrDefault(p => p.ParameterName == param.name );
if (customAttribute != null)
{
param.@default = customAttribute.Value;
}
}
}
}
}
Not quite the same but... we use the following which discovers the default/optional values specified in the action signature, no attribute required.
The problem for us with the above mechanism is that the attr doesn't actually enforce the value it just affects the docs. So it's pretty easy for the swagger to "lie" about the api if you're not extremely diligent about keeping signatures, attributes and DTO ctors in sync.
Lastly would it not be "better" to use the DefaultValueAttribute in System.ComponentModel instead of creating a new one?
public class SwaggerDefaultValues : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
try
{
var parameterValuePairs =
apiDescription.ActionDescriptor.GetParameters()
.Where(parameter => parameter.DefaultValue != null)
.ToDictionary(parameter => parameter.ParameterName, parameter => parameter.DefaultValue);
foreach (var param in operation.parameters)
{
object defaultValue;
if (parameterValuePairs.TryGetValue(param.name, out defaultValue))
param.@default = defaultValue;
}
}
catch { }
}
}
Is Swashbuckle available for EclipseEE?
This extension of @AndyPook's code also flags optional parameters as "required": false in the Swagger file, as well as including the default value.
public class UpdateOptionalParamatersWithDefaultValues : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
return;
var parameterValuePairs =
apiDescription.ActionDescriptor.GetParameters()
.Where(parameter => parameter.DefaultValue != null)
.ToDictionary(parameter => parameter.ParameterName, parameter => parameter.DefaultValue);
foreach (var param in operation.parameters)
{
object defaultValue;
if (parameterValuePairs.TryGetValue(param.name, out defaultValue))
{
param.@default = defaultValue;
param.@required = false;
}
}
}
}
I'm trying to reproduce this (and extend it to variable schema values) with the current code -- I mean, with Swashbuckle.AspNetCore 5.0.0 .. customAttribute is never non-null, and when I run it CustomAttributes is always empty. Was there some magic to the attribute name used?? I could us some tips
```c#
public class AddSettings : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
foreach (var param in operation.Parameters)
{
var actionParam = context.ApiDescription.ActionDescriptor.Parameters.First(p => p.Name == param.Name);
if (actionParam != null)
{
var customAttribute = ((ControllerParameterDescriptor)actionParam).ParameterInfo.CustomAttributes
.OfType
.FirstOrDefault();
if (customAttribute != null)
{
foreach (SwaggerParameter parameter in customAttribute.GetSwaggerParameters())
{
parameter.ApplyTo(param);
}
}
}
}
}
used like:
```c#
[HttpPut("{containername}/contentfiles/{fileName}")]
[SwaggerResponseHeader(StatusCodes.Status201Created, "Location", "string", "Location of the newly created resource")]
[SwaggerParameterDescriptions("containername", "{\"MinLength\":3,\"MaxLength\":63,\"Pattern\":\"^[a-z0-9]+(-[a-z0-9]+)*$\"}")]
[SwaggerParameterDescriptions("fileName", "{\"MinLength\":1,\"MaxLength\":75,\"Pattern\":\"\\\\S\"}")]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(ErrorResponse), StatusCodes.Status503ServiceUnavailable)]
public ActionResult Put(string containername, string fileName, IFormFile fileData)
the rest of the classes, in case it is desired:
```c#
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public class SwaggerParameterDescriptions : Attribute
{
// just these for now
public enum Description
{
Default,
MinLength,
MaxLength,
Pattern
}
public string ParameterName { get; set; }
public Dictionary<Description, dynamic> Settings { get; set; }
public SwaggerParameterDescriptions(string parameterName, string json)
{
Dictionary<string, dynamic> dict = JsonSerializer
.Deserialize<Dictionary<string, dynamic>>(json);
Dictionary<Description, dynamic> settings = dict.Entries()
.ToDictionary(entry => (Description)Enum.Parse(typeof(Description), (string)entry.Key),
entry => entry.Value);
ParameterName = parameterName;
Settings = settings;
}
public IEnumerable<SwaggerParameter> GetSwaggerParameters()
{
return Settings.Keys.Select(key =>
new SwaggerParameter { ParameterName = key, Value = Settings[key] });
}
}
public class SwaggerParameter
{
public Description ParameterName { get; set; }
public dynamic Value { get; set; }
public void ApplyTo(OpenApiParameter param)
{
switch (ParameterName)
{
case Description.Default:
param.Schema.Default = Value;
break;
case Description.MinLength:
param.Schema.MinLength = (int)Value;
break;
case Description.MaxLength:
param.Schema.MaxLength = (int)Value;
break;
case Description.Pattern:
param.Schema.Pattern = $"{Value}";
break;
default:
throw new InvalidOperationException();
}
}
}
```
Most helpful comment
This extension of @AndyPook's code also flags optional parameters as
"required": falsein the Swagger file, as well as including the default value.