C# 如何在.NET核心web api中向swagger UI添加上载按钮?
我有一个带有swagger(使用Swashback)的ASP.net核心web API Web API中的一个操作是文件上载操作:C# 如何在.NET核心web api中向swagger UI添加上载按钮?,c#,asp.net-core,swagger,swagger-ui,asp.net-core-webapi,C#,Asp.net Core,Swagger,Swagger Ui,Asp.net Core Webapi,我有一个带有swagger(使用Swashback)的ASP.net核心web API Web API中的一个操作是文件上载操作: [Produces("application/json")] [Route("[controller]")] public class FilesController : Controller { [HttpPost] public void Post(IFormFile file) { ... } } 当我在swa
[Produces("application/json")]
[Route("[controller]")]
public class FilesController : Controller
{
[HttpPost]
public void Post(IFormFile file)
{
...
}
}
当我在swagger UI中查找该操作时,它让我填写IFormFile
的所有字段,这不是我测试API所要做的
那么我如何才能将上载按钮添加到Swagger UI?首先添加一个使用多部分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);
}
除了@Nick的答案外,我还必须对AspNet core 2进行2项更改 1] 更新的GetOperationId() 现在,所有操作ID都包含API前缀以及后缀中的方法。因此,我在ActionName中静态添加了API和Post
public static string GetOperationId<T>(string actionName) where T : ControllerBase => $"Api{GetControllerName<T>()}{actionName}Post";
适用于寻找开放式api实现的任何人
/// <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
{
}
}
文件上传应在请求正文中生成,如下所示
对于具有多个需要上载文件的端点并希望为其文件上载参数使用更通用/描述性/个人名称的任何人:
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 }
};
}
}
}
然后我就这样使用它:
public async Task<IActionResult> CreateFromFileAsync(
[FromForm, FormFileDescriptor("Project JSON file", "The project as a JSON file", true, 52_428_800)] IFormFile projectJsonFileFile,
CancellationToken cancellationToken)
{
公共异步任务CreateFromFileAsync(
[FromForm,FormFileDescriptor(“项目JSON文件”,“项目作为JSON文件”,true,52_428_800)]FormFile项目JSonfile,
取消令牌(取消令牌)
{
确定操作名称的好帮手方法。我看到许多人使用硬编码的名称,总是很难弄清楚为什么名称发生变化时某些东西不起作用。
[HttpPost]
[Route("PostFile")]
[FileUploadOperation.FileContentType]
public IActionResult PostFile(IFormFile uploadedFile)
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 }
};
}
}
}
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; }
}
public async Task<IActionResult> CreateFromFileAsync(
[FromForm, FormFileDescriptor("Project JSON file", "The project as a JSON file", true, 52_428_800)] IFormFile projectJsonFileFile,
CancellationToken cancellationToken)
{