Is there a way to exclude/remove properties from your example value?
I'm using XML comments on my models to provide information on the swagger page with c.IncludeXmlComments
I use the ///<example>Example Value</example> XML tag to set the example values. My request model does not require all the fields to be set by default, but if I don't set an example XML tag the example value is translated to it's type. Looking like this
{
"ID": "string",
"ExampleSetValue": "Example Value"
}
And I want my example value to only contain the ExampleSetValue property so my example value looks like this
{
"ExampleSetValue": "Example Value"
}
This is the swagger setup in my startup
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", openApiInfo);
c.AddSecurityDefinition("Bearer", openApiSecurityScheme);
c.AddSecurityRequirement(openApiSecurityRequirement);
// Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
My Request Model
public class CreateRequest
{
/// <summary>
/// ID of the user if available
/// </summary>
public string ID { get; set; }
/// <summary>
/// ExampleSetValue
/// </summary>
/// <example>Example Value</example>
public string ExampleSetValue { get; set; }
}
Xml comment seems cannot be used to exclude the property.
If you want to ignore the property, I suggest you use the [System.Text.Json.Serialization.JsonIgnore] attribute. But in this way it will also make your property hidden when you serialize or deserialize the data.
public class CreateRequest
{
/// <summary>
/// ID of the user if available
/// </summary>
[JsonIgnore] //add this..........
public string ID { get; set; }
/// <summary>
/// ExampleSetValue
/// </summary>
/// <example>Example Value</example>
public string ExampleSetValue { get; set; }
}
If you only want to exclude the property from example value, you need custom ISchemaFilter:
Model:
public class CreateRequest
{
/// <summary>
/// ID of the user if available
/// </summary>
[IgnoreDataMember] //add this..........
public string ID { get; set; }
/// <summary>
/// ExampleSetValue
/// </summary>
/// <example>Example Value</example>
public string ExampleSetValue { get; set; }
}
Custom ISchemaFilter:
public class MySwaggerSchemaFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
if (schema?.Properties == null)
{
return;
}
var ignoreDataMemberProperties = context.Type.GetProperties()
.Where(t => t.GetCustomAttribute<IgnoreDataMemberAttribute>() != null);
foreach (var ignoreDataMemberProperty in ignoreDataMemberProperties)
{
var propertyToHide = schema.Properties.Keys
.SingleOrDefault(x => x.ToLower() == ignoreDataMemberProperty.Name.ToLower());
if (propertyToHide != null)
{
schema.Properties.Remove(propertyToHide);
}
}
}
}
Register the filter:
services.AddSwaggerGen(c =>
{
//......
c.SchemaFilter<MySwaggerSchemaFilter>();
});
I found a different approach which suited my case exactly how I wanted.
You can set an example object on your schema which is of type OpenApiObject.
I Created a Document filter to which loops over the schemas in my swagger.
public class AddExamplesFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var schema in context.SchemaRepository.Schemas)
{
schema.Value.Example = ExampleManager.GetExample(schema.Key);
}
}
}
Created the ExampleManager class which returns the example object I want according to the schema name.
public static OpenApiObject GetExample(string requestModelName)
{
return requestModelName switch
{
"ObjectExample" => ObjectExample.GetExample(),
_ => null
};
}
And as the final step created an example class which corresponds exactly on how I want my example to look like. Where I used the OpenApiObject class
public static class ObjectExample
{
public static OpenApiObject GetExample()
{
return new OpenApiObject
{
["ObjectInsideObject"] = new OpenApiObject
{
["Name"] = new OpenApiString("name"),
},
["ArrayWithObjects"] = new OpenApiArray
{
new OpenApiObject
{
["Object"] = new OpenApiObject
{
["integer"] = new OpenApiInteger(15),
},
["Description"] = new OpenApiString("description")
}
},
["date"] = new OpenApiDate(new DateTime().AddMonths(1)),
["Description"] = new OpenApiString("description"),
};
}
}
Final look on Swagger page
Related
I have a ASP.NET Core Web API 6.0 project containing an endpoint which expects XML in its POST body (application/xml). I have created an attribute as follows:
using System;
/// <summary>
/// Used to mark an API for Swagger as having a raw XML body (Swagger creates an inout field).
/// </summary>
public class XmlPayloadAttribute : Attribute
{
/// <summary>
///
/// </summary>
public XmlPayloadAttribute()
{
ParameterName = "payload";
Required = true;
MediaType = "application/xml";
Format = "xml";
}
/// <summary>
///
/// </summary>
public string Format { get; set; }
/// <summary>
///
/// </summary>
public string MediaType { get; set; }
/// <summary>
///
/// </summary>
public bool Required { get; set; }
/// <summary>
///
/// </summary>
public string ParameterName { get; set; }
}
...and a filter as follows:
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen; // Swashbuckle.AspNetCore version 6.2.3
public class XmlPayloadFilter : IOperationFilter
{
/// <summary>
///
/// </summary>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var attribute = context.MethodInfo.GetCustomAttributes(typeof(XmlPayloadAttribute), false).FirstOrDefault();
if (attribute == null)
{
return;
}
operation.RequestBody = new OpenApiRequestBody() { Required = true };
var xml = #"<MyObject><Something/></MyObject>"; // How do I get this XML to appear as my example, verbatim?
operation.RequestBody.Content.Add("application/xml", new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "string",
Example = new OpenApiString(xml) // This doesn't work!
},
Example = new OpenApiString(xml) // This doesn't work either!
});
}
}
I add the [XmlPayload] annotation to my endpoint as shown here:
using System;
using System.IO;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
/// <summary>
/// My controller.
/// </summary>
[ApiController]
public class MyController : ControllerBase
{
/// <summary>
/// My Method.
/// </summary>
[Route("MyMethod")]
[HttpPost]
[XmlPayload]
public IActionResult MyMethod()
{
using var reader = new StreamReader(Request.Body);
var content = reader.ReadToEndAsync().Result;
// I deserialize and process the xml here
return new ContentResult
{
Content = /* My XML response goes here */,
ContentType = "application/xml",
StatusCode = StatusCodes.Status200OK
};
}
}
Swagger correctly creates an input field for the post body, and correctly understands that the content type should be "application/xml", but populates the field with:
<?xml version="1.0" encoding="UTF-8"?>
<!-- XML example cannot be generated; root element name is undefined -->
If I use Name as follows:
operation.RequestBody = new OpenApiRequestBody() { Required = true };
var xml =
#"<s:MyRoot xmlns:s=""http://myschema"">
<s:MyTag>
</s:MyTag>
</s:MyRoot>";
operation.RequestBody.Content.Add("application/xml", new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Xml = new OpenApiXml
{
Name = "MyRoot",
Namespace = new Uri("http://myschema")
},
Example = new OpenApiString(xml, true)
},
});
I get:
<?xml version="1.0" encoding="UTF-8"?>
<MyRoot xmlns="http://myschema/"><s:MyRoot
xmlns:s="http://myschema">
<s:MyTag>
</s:MyTag>
</s:MyRoot></MyRoot>
I get the same result with or without Namespace and with or without setting the isExplicit parameter of OpenApiString to either true or false.
How do I get Swagger to display my XML string verbatim (how do I fix my C#)?
operation.RequestBody.Content.Add("application/xml", new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "string",
Xml = new OpenApiXml()
{
Name = "<RootElementNameHere>"
},
Example = new OpenApiString(xml) // This doesn't work!
},
Example = new OpenApiString(xml) // This doesn't work either!
});
In order for the example to be displayed verbatim (without escaping), you need to add a DTO and generate its schema.
In the attribute class, add the properties Type and Example
[AttributeUsage(validOn: AttributeTargets.Method, AllowMultiple = true)]
public class XmlPayloadAttribute : Attribute
{
public XmlPayloadAttribute(Type type, string? example = null)
{
ParameterName = "payload";
Required = true;
MediaType = "application/xml";
Format = "xml";
ParameterExample = example;
ParameterType = type;
}
public string Format { get; set; }
public string MediaType { get; set; }
public bool Required { get; set; }
public string ParameterName { get; set; }
public Type ParameterType { get; set; }
public string? ParameterExample { get; set; }
}
In the Filter, add all the examples specified through the attributes to the method
public class XmlPayloadFilter : IOperationFilter
{
private string FormatXml(string xml)
{
try
{
XDocument doc = XDocument.Parse(xml);
return doc.ToString();
}
catch (Exception)
{
return xml;
}
}
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var attributes = context.MethodInfo.GetCustomAttributes(typeof(XmlPayloadAttribute), false);
if (attributes == null || attributes.Length == 0)
{
return;
}
operation.RequestBody = new OpenApiRequestBody() { Required = true };
foreach (XmlPayloadAttribute attribute in attributes)
{
if (operation.RequestBody.Content.TryGetValue("application/xml", out var type))
{
type.Examples.Add($"{attribute.ParameterType.Name}", new OpenApiExample() { Value = new OpenApiString(FormatXml(attribute.ParameterExample)) });
}
else
{
operation.RequestBody.Content.Add(
"application/xml",
new OpenApiMediaType
{
Schema = context.SchemaGenerator.GenerateSchema(attribute.ParameterType, context.SchemaRepository),
Examples = new Dictionary<string, OpenApiExample>
{
{
$"{attribute.ParameterType.Name}", new OpenApiExample() { Value = new OpenApiString(FormatXml(attribute.ParameterExample)) }
}
}
}
);
}
}
}
}
Use attribute like this
[HttpPost("post")]
[XmlPayload(typeof(DTOClass1), "<ExampleXml1>")]
[XmlPayload(typeof(DTOClass2), "<ExampleXml2>")]
[XmlPayload(typeof(DTOClass3), "<ExampleXml3>")]
public async Task<IActionResult> post()
{
...
}
I am using the summary and example tags for the swagger documentation.
I have a problem with the tag, it's not recognized by swagger when I use array :
I use swashbuckle.aspnetcore package Nuget.
Example :
[DataContract]
public class HeaderResponse
{
/// <summary>
/// Statut code
/// </summary>
///<example>400</example>
[DataMember]
public int StatusCode { get; set; }
/// <summary>
/// Title
/// </summary>
/// <example>Erreur sur methode1</example>
[DataMember]
public string Title { get; set; }
/// <summary>
/// List of errors
/// </summary>
///<example>["entry1", "entry2", "entry3"]</example>
[DataMember]
public List<string> ErrorList { get; } = new List<string>();
}
On swagger documentation, array is not interpreted :
I found others solutions by using ISchemaFilter like this :
public class SwaggerExcludeFilter : ISchemaFilter
{
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
switch (context.Type.Name)
{
case "HeaderResponse":
foreach (var property in schema.Properties)
{
if (property.Value.Type == "array")
{
var array = new OpenApiArray();
array.Add(new OpenApiString("item1"));
array.Add(new OpenApiString("item2"));
array.Add(new OpenApiString("item3"));
property.Value.Example = array;
}
}
break;
}
}
}
Is there no other way than to use ISchemaFilter to handle tags of type array?
You can try to do like this:
public List<string> ErrorList { get; } = new List<string>{"entry1", "entry2", "entry3"};
or:
[DataContract]
public class HeaderResponse
{
public HeaderResponse()
{
ErrorList = new List<string> {"entry1", "entry2", "entry3" };
}
/// <summary>
/// Statut code
/// </summary>
///<example>400</example>
[DataMember]
public int StatusCode { get; set; }
/// <summary>
/// Title
/// </summary>
/// <example>Erreur sur methode1</example>
[DataMember]
public string Title { get; set; }
/// <summary>
/// List of errors
/// </summary>
[DataMember]
public List<string> ErrorList { get; set; }
}
Here is a demo:
[HttpPost("TestPar")]
public IActionResult TestPar(HeaderResponse h)
{
return Json(h);
}
result:
The quotes must be escaped using "
/// <summary>
/// List of errors
/// </summary>
///<example>["entry1","entry2","entry3"]</example>
[DataMember]
public List<string> ErrorList { get; } = new List<string>();
I have the same problem and I try this. you can add a condition on the name of the property and add the null return in order not to lose the example on the other properties. But the best solution would be to be able to find a generic solution. How is it possible to get the example tag and the property name with ISchemaFilter ? This is my solution :
public void Apply(OpenApiSchema schema, SchemaFilterContext context)
{
schema.Example = GetExampleOrNullFor(schema, context);
}
private IOpenApiAny GetExampleOrNullFor(OpenApiSchema schema, SchemaFilterContext context)
{
switch (context.Type.Name)
{
case "MyClass":
foreach (var property in schema.Properties)
{
//Array property, which wraps its elements
if (property.Value.Type == "array")
{
if (property.Key == "MyProperty1")
{
var array = new OpenApiArray();
array.Add(new OpenApiString("item1"));
array.Add(new OpenApiString("item2"));
property.Value.Example = array;
}
else if (property.Key == "MyProperty2")
{
var array = new OpenApiArray();
array.Add(new OpenApiString("item1"));
property.Value.Example = array;
}
}
}
return null;
default:
return null;
}
}
Support for examples using XML comments on arrays was added in Swashbuckle.AspNetCore version 6.0.0.
It's mentioned in this GitHub issue and their documentation shows the following example:
/// <summary>
/// The sizes the product is available in
/// </summary>
/// <example>["Small", "Medium", "Large"]</example>
public List<string> Sizes { get; set; }
I have a data model consisting of a couple of properties and methods, this data model inherits from an interface. However, I've now created a static extension and would like to set one of my model properties to the result of that extension method.
These are my models and extensions:
PublishedContentModel:
namespace CMS.Core.Models
{
public class PublishedContentModel : IPublishedContent
{
/// <summary>
/// The id of this node.
/// </summary>
public Int32 Id { get; set; }
/// <summary>
/// The name of this node.
/// </summary>
public String Name { get; set; }
/// <summary>
/// The url path.
/// </summary>
public String Path { get; set; }
/// <summary>
/// This nodes document id.
/// </summary>
public Int32 DocumentId { get; set; }
/// <summary>
/// The parent node's id.
/// </summary>
public IPublishedContent Parent { get; set; }
/// <summary>
/// The collection of this node's children.
/// </summary>
public IEnumerable<IPublishedContent> Children { get; set; }
/// <summary>
/// The collection of ancestors.
/// </summary>
public IEnumerable<IPublishedContent> Ancestors ()
{
IEnumerable<IPublishedContent> colContentNodes = ContentService.GetAllContentNodes();
List<IPublishedContent> colAncestors = new List<IPublishedContent>();
foreach (IPublishedContent objContentNode in colContentNodes.Where(x => x.Descendants().Any(y => y.Id == Id)))
{
colAncestors.Add(objContentNode);
}
return colAncestors;
}
/// <summary>
/// The collection of all nodes under this.
/// </summary>
public IEnumerable<IPublishedContent> Descendants ()
{
// - Get the children and create a collection for descendants.
IEnumerable<IPublishedContent> colChildren = Children;
List<IPublishedContent> colDescendants = new List<IPublishedContent>();
if (colChildren.Any())
{
colDescendants = colChildren.ToList();
foreach (IPublishedContent objChild in colChildren)
{
IEnumerable<IPublishedContent> colChildsChildren = objChild.Children;
// - Check if this node has children.
if (colChildsChildren.Any())
{
colDescendants.AddRange(colChildsChildren);
}
}
}
return colDescendants;
}
}
}
ContentModelExtensions:
namespace CMS.Core.Models
{
public static class ContentModelExtensions
{
public static IEnumerable<IPublishedContent> Children (this IPublishedContent Content)
{
IEnumerable<IPublishedContent> colContentNodes = ContentService.GetAllContentNodes();
List<IPublishedContent> colChildren = new List<IPublishedContent>();
foreach (IPublishedContent objContentNode in colContentNodes.Where(x => x.Parent != null))
{
colChildren.Add(objContentNode);
}
return colChildren;
}
public static IEnumerable<T> Children<T> (this IPublishedContent Content) where T : class, IPublishedContent
{
IEnumerable<IPublishedContent> colContentNodes = ContentService.GetAllContentNodes();
List<T> colChildren = new List<T>();
foreach (T objContentNode in colContentNodes.Where(x => x.Parent != null))
{
colChildren.Add(objContentNode);
}
return colChildren;
}
}
}
The issue with this code is that I can't do Children.Any() seeing as the IEnumerable for this is null. I tried fixing this by setting the Children property when it gets sent to the view, like this:
objContentNode = new PublishedContentModel ()
{
Id = Convert.ToInt32(objReader["Id"]),
Name = Convert.ToString(objReader["Name"]),
Parent = GetContentNodeById(Convert.ToInt32(objReader["ParentId"])),
Path = Convert.ToString(objReader["Path"]),
DocumentId = Convert.ToInt32(objReader["DocumentId"]),
Children = ContentModelExtensions.Children(objContentNode)
};
Put that just makes the connection timeout, any help would be appreciated. Let me know if I haven't explained my issue well enough.
TLDR:
I want to set public IEnumerable<IPublishedContent> Children { get; set; } to the result of ContentModelExtensions.Children()
I have an ASP.net core web API with swagger (using swashbuckle).
One of the actions in the Web API is a file upload action:
[Produces("application/json")]
[Route("[controller]")]
public class FilesController : Controller
{
[HttpPost]
public void Post(IFormFile file)
{
...
}
}
When I look up that action in the swagger UI it let's me fill in all the fields of IFormFile, which is not what I want to do to test my API.
So how can I add an upload button to the Swagger UI?
For anyone looking for an open api implementation
/// <summary>
/// Add extra parameters for uploading files in swagger.
/// </summary>
public class FileUploadOperation : IOperationFilter
{
/// <summary>
/// Applies the specified operation.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="context">The context.</param>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var isFileUploadOperation =
context.MethodInfo.CustomAttributes.Any(a => a.AttributeType == typeof(FileContentType));
if (!isFileUploadOperation) return;
operation.Parameters.Clear();
var uploadFileMediaType = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "object",
Properties =
{
["uploadedFile"] = new OpenApiSchema()
{
Description = "Upload File",
Type = "file",
Format = "formData"
}
},
Required = new HashSet<string>(){ "uploadedFile" }
}
};
operation.RequestBody = new OpenApiRequestBody
{
Content = { ["multipart/form-data"] = uploadFileMediaType }
};
}
/// <summary>
/// Indicates swashbuckle should consider the parameter as a file upload
/// </summary>
[AttributeUsage(AttributeTargets.Method)]
public class FileContentType : Attribute
{
}
}
Decorate controller using FileContentType attribute
[HttpPost]
[Route("PostFile")]
[FileUploadOperation.FileContentType]
public IActionResult PostFile(IFormFile uploadedFile)
File upload should be generated in the Request body like below..
First add a operation filter that consumes the multipart formdata.
public class FileUploadOperation : IOperationFilter
{
private readonly IEnumerable<string> _actionsWithUpload = new []
{
//add your upload actions here!
NamingHelpers.GetOperationId<FilesController>(nameof(FilesController.Post))
};
public void Apply(Operation operation, OperationFilterContext context)
{
if (_actionsWithUpload.Contains(operation.OperationId) )
{
operation.Parameters.Clear();
operation.Parameters.Add(new NonBodyParameter
{
Name = "file",
In = "formData",
Description = "Upload File",
Required = true,
Type = "file"
});
operation.Consumes.Add("multipart/form-data");
}
}
}
/// <summary>
/// Refatoring friendly helper to get names of controllers and operation ids
/// </summary>
public class NamingHelpers
{
public static string GetOperationId<T>(string actionName) where T : Controller => $"{GetControllerName<T>()}{actionName}";
public static string GetControllerName<T>() where T : Controller => typeof(T).Name.Replace(nameof(Controller), string.Empty);
}
Now you should add your actions to the _actionWithUpload array!
Note that I added the extesnions only to have a refactoring friendly filter.
Last but not least, make sure the operation filter is added to the options of swagger. So add options.OperationFilter<FileUploadOperation>(); to your swagger options and done.
Full example:
services.AddSwaggerGen(options =>
{
options.SwaggerDoc(Version, new Info
{
Title = Title,
Version = Version
}
);
var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, $"{_webApiAssemblyName}.xml");
options.IncludeXmlComments(filePath);
options.DescribeAllEnumsAsStrings();
//this is the step where we add the operation filter
options.OperationFilter<FileUploadOperation>();
});
In addition to #Nick's answer, I have to make 2 changes for AspNet core 2.
1] Updated GetOperationId()
Now all operationIds contain API prefix as well as Method in the suffix. So I have statically added API & Post in ActionName.
public static string GetOperationId<T>(string actionName) where T : ControllerBase => $"Api{GetControllerName<T>()}{actionName}Post";
2] Remove only file parameter
Instead of removing all parameters for that action, I want to remove only the file parameter.
var fileParameter = operation.Parameters.FirstOrDefault(x => x.Name == "file" && x.In == "body");
if (fileParameter != null)
{
operation.Parameters.Remove(fileParameter);
...
}
For anyone who has more than one endpoint that needs to upload files and want to use more generic / descriptive / individual names for their file upload parameters:
public class SwaggerFileOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var fileParams = context.MethodInfo.GetParameters().Where(p => p.ParameterType.FullName?.Equals(typeof(Microsoft.AspNetCore.Http.IFormFile).FullName) == true);
if (fileParams.Any() && fileParams.Count() == 1)
{
var title = "The file to be uploaded";
var description = "The file to be uploaded";
int? maxLength = 5_242_880;
bool required = true;
var descriptionAttribute = fileParams.First().CustomAttributes.FirstOrDefault(a => a.AttributeType == typeof(FormFileDescriptorAttribute));
if (descriptionAttribute?.ConstructorArguments.Count > 3)
{
title = descriptionAttribute.ConstructorArguments[0].Value.ToString();
description = descriptionAttribute.ConstructorArguments[1].Value.ToString();
required = (bool)descriptionAttribute.ConstructorArguments[2].Value;
maxLength = (int)descriptionAttribute.ConstructorArguments[3].Value;
}
var uploadFileMediaType = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "object",
Properties =
{
[fileParams.First().Name] = new OpenApiSchema()
{
Description = description,
Type = "file",
Format = "binary",
Title = title,
MaxLength = maxLength
}
}
}
};
if (required)
{
uploadFileMediaType.Schema.Required = new HashSet<string>() { fileParams.First().Name };
}
operation.RequestBody = new OpenApiRequestBody
{
Content = { ["multipart/form-data"] = uploadFileMediaType }
};
}
}
}
I created an attribute to add more description for my file upload:
public class FormFileDescriptorAttribute : Attribute
{
public FormFileDescriptorAttribute(string title, string description, bool required, int maxLength)
{
Title = title;
Description = description;
Required = required;
MaxLength = maxLength;
}
public string Title { get; set; }
public string Description { get; set; }
public int MaxLength { get; set; }
public bool Required { get; set; }
}
And then I use it as such:
public async Task<IActionResult> CreateFromFileAsync(
[FromForm, FormFileDescriptor("Project JSON file", "The project as a JSON file", true, 52_428_800)] IFormFile projectJsonFileFile,
CancellationToken cancellationToken)
{
controller:
[HttpPost]
public async Task<string> Post([FromForm] ImageUploadVW imageUpload)
{
if(imageUpload.Image.Length > 0)
{
string path = _webHostEnvironment.WebRootPath + "\\uploads\\";
if (!Directory.Exists(path))
{
Directory.CreateDirectory(path);
}
using (FileStream fileStream = System.IO.File.Create(path +
imageUpload.Image.FileName))
{
imageUpload.Image.CopyTo(fileStream);
fileStream.Flush();
return "Upload Done";
}
}
else
{
return "failed to Upload Image";
}
public class ImageUploadVW
{
public IFormFile Image { get; set; }
}
I replace only file type parameters and not use clear all parameters.
/// <summary>
/// Add extra parameters for uploading files in swagger.
/// </summary>
public class FileUploadSawggerOperationFilter : IOperationFilter
{
/// <summary>
/// Applies the specified operation.
/// </summary>
/// <param name="operation">The operation.</param>
/// <param name="context">The context.</param>
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var fileTypeParameters = context.MethodInfo.GetParameters().Where(c => c.ParameterType == typeof(IFormFile));
if (!fileTypeParameters.Any()) return;
var uploadFileMediaType = new OpenApiMediaType()
{
Schema = new OpenApiSchema()
{
Type = "object",
}
};
foreach (var fileTypeParameter in fileTypeParameters)
{
var operationParameter = operation.Parameters.SingleOrDefault(c => c.Name == fileTypeParameter.Name);
if (operationParameter is not null) operation.Parameters.Remove(operationParameter);
uploadFileMediaType.Schema.Properties.Add(fileTypeParameter.Name, new OpenApiSchema()
{
Description = "Upload File",
Type = "file",
Format = "formData"
});
}
operation.RequestBody = new OpenApiRequestBody
{
Content = { ["multipart/form-data"] = uploadFileMediaType }
};
}
}
I use the query parameters from the "message", and these parameters can be more than 100 and optional. The siganture should stay in this form.
So my question is, how can I document some of the query parameters, to show in swagger UI, and stay try-able?
/// <summary>
/// Callback Endpoint
/// </summary>
/// <returns>HTTP 200 <see cref="HttpStatusCode.OK"/>.</returns>
/// <param name="message">The message</param>
[HttpGet]
[SwaggerParameter("Something", "It is something")]
[Route("endpoint", Name = nameof(Endpoint))]
public virtual async Task<HttpResponseMessage> Endpoint(HttpRequestMessage message)
To ignore the "HttpRequestMessage" I use an OperationFilter:
ASP.Net Web API Swashbuckle how to ignore HttpRequestMessage
config.EnableSwagger(swagger =>
{
swagger.OperationFilter<IgnoreHttpRequestMessage>();
swagger.OperationFilter<SwaggerParameterAttributeHandler>();
[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 Description { get; private set; }
public bool Required { get; set; } = false;
}
public class SwaggerParameterAttributeHandler : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
// Get all SwaggerParameterAttributes on the method
var attributes = apiDescription.ActionDescriptor.GetCustomAttributes<SwaggerParameterAttribute>(true);
if (operation.parameters == null)
{
operation.parameters = new List<Parameter>();
}
foreach (var attribute in attributes)
{
var parameter = operation.parameters.FirstOrDefault(p => p.name == attribute.Name);
if (parameter != null)
{
parameter.required = attribute.Required;
}
else
{
operation.parameters.Add(new Parameter()
{
name = attribute.Name,
description = attribute.Description,
type = "string",
required = attribute.Required
});
}
}
}
}
I got a pointment, and get the solution for this question.
I leave this article here, maybe somebody need it.
The problem with the new Parameter, the "in" need to filled out. After it on the swaggerUI the tryOut button works perfectly.
operation.parameters.Add(new Parameter()
{
name = attribute.Name,
description = attribute.Description,
type = "string",
required = attribute.Required,
#in = "query"
});