猿问

使用 Swashbuckle Swagger 上传文件

我有一个带有 Swagger 3.0 的 ASP.NET Core 2 应用程序,我想通过从 Swagger UI 上传文件来测试 POST 操作。


文件上传控制器:


[ApiController]

public class FileUploadController : ApiControllerBase

{

    private readonly IFileUploader _fileUploader;


    public FileUploadController(IFileUploader fileUploader, ILogger<FileUploadController> logger) : base(logger)

    {

        _fileUploader = fileUploader;

    }


    [HttpPost]

    [Route(nameof(UploadFile))]

    public FileUploadResult UploadFile(IFormFile uploadedFile)

    {

        return _FileUploader.UploadFile(Request.Form.Files[0]);

    }

}

阅读 Swagger 文档和其他主题,我发现我需要自定义 IOperationFilter,所以这就是我想出的:


public class FileUploadOperationFilter : IOperationFilter

{

    public void Apply(Operation operation, OperationFilterContext context)

    {

        if (operation.OperationId.ToLower() == "apifileuploaduploadfilepost")

        {

            operation.Parameters.Add(new NonBodyParameter()

            {

                Name = "file",

                In = "formData",

                Description = "Upload File",

                Required = true,

                Type = "file"

            });

            operation.Consumes.Add("multipart/form-data");

        }

    }

}

它在 Swagger 启动类中注册:


                options.OperationFilter<FileUploadOperationFilter>();

因此,来自 IFormFile 的所有字段都消失了,而是有上传文件控件,因此我可以选择“试用”按钮,选择一个文件,但是在按“执行”后,我收到以下错误:

web api 本身不会崩溃,也不会抛出任何错误。我在调试期间注意到的 -FileUploadController达到了 的构造函数但没有触发 FileUpload 方法。


繁星coding
浏览 352回答 3
3回答

largeQ

你能试试这个操作过滤器吗:/// <summary>/// Filter to enable handling file upload in swagger/// </summary>public class FormFileSwaggerFilter : IOperationFilter{&nbsp; &nbsp; private const string formDataMimeType = "multipart/form-data";&nbsp; &nbsp; private static readonly string[] formFilePropertyNames =&nbsp; &nbsp; &nbsp; &nbsp; typeof(IFormFile).GetTypeInfo().DeclaredProperties.Select(p => p.Name).ToArray();&nbsp; &nbsp; /// <summary>&nbsp; &nbsp; /// Applies the specified operation.&nbsp; &nbsp; /// </summary>&nbsp; &nbsp; /// <param name="operation">The operation.</param>&nbsp; &nbsp; /// <param name="context">The context.</param>&nbsp; &nbsp; public void Apply(Operation operation, OperationFilterContext context)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; var parameters = operation.Parameters;&nbsp; &nbsp; &nbsp; &nbsp; if (parameters == null || parameters.Count == 0) return;&nbsp; &nbsp; &nbsp; &nbsp; var formFileParameterNames = new List<string>();&nbsp; &nbsp; &nbsp; &nbsp; var formFileSubParameterNames = new List<string>();&nbsp; &nbsp; &nbsp; &nbsp; ProcessActionParameters(context, ref formFileParameterNames, ref formFileSubParameterNames);&nbsp; &nbsp; &nbsp; &nbsp; if (!formFileParameterNames.Any()) return;&nbsp; &nbsp; &nbsp; &nbsp; var consumes = operation.Consumes;&nbsp; &nbsp; &nbsp; &nbsp; consumes.Clear();&nbsp; &nbsp; &nbsp; &nbsp; consumes.Add(formDataMimeType);&nbsp; &nbsp; &nbsp; &nbsp; ProcessParameters(ref parameters, formFileSubParameterNames);&nbsp; &nbsp; &nbsp; &nbsp; ProcessFormFileParameters(formFileParameterNames, ref parameters);&nbsp; &nbsp; }&nbsp; &nbsp; private static void ProcessFormFileParameters(List<string> formFileParameterNames, ref IList<IParameter> parameters)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var formFileParameter in formFileParameterNames)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parameters.Add(new NonBodyParameter()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Name = formFileParameter,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Type = "file",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; In = "formData"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; private static void ProcessActionParameters(OperationFilterContext context, ref List<string> formFileParameterNames,&nbsp; &nbsp; &nbsp; &nbsp; ref List<string> formFileSubParameterNames)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var actionParameter in context.ApiDescription.ActionDescriptor.Parameters)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var properties =&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; actionParameter.ParameterType.GetProperties()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Where(p => p.PropertyType == typeof(IFormFile))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .Select(p => p.Name)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .ToArray();&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (properties.Length != 0)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formFileParameterNames.AddRange(properties);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formFileSubParameterNames.AddRange(properties);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (actionParameter.ParameterType != typeof(IFormFile)) continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; formFileParameterNames.Add(actionParameter.Name);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; private static void ProcessParameters(ref IList<IParameter> parameters, List<string> formFileSubParameterNames)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; foreach (var parameter in parameters.ToArray())&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (!(parameter is NonBodyParameter) || parameter.In != "formData") continue;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (formFileSubParameterNames.Any(p => parameter.Name.StartsWith(p + "."))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; || formFilePropertyNames.Contains(parameter.Name))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; parameters.Remove(parameter);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}

喵喔喔

表单中的键应该与方法中的参数匹配,在过滤器中重命名:operation.Parameters.Add(new NonBodyParameter()&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Name = "uploadedFile",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; In = "formData",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Description = "Upload File",&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Required = true,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Type = "file"&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; });

冉冉说

通过从参数中删除 IFormFile 上传文件来解决:&nbsp; &nbsp; [HttpPost][Route(nameof(UploadFile))]public FileUploadResult UploadFile(){&nbsp; &nbsp; return _FileUploader.UploadFile(Request.Form.Files[0]);}
随时随地看视频慕课网APP
我要回答