【问题标题】:How can I add an upload button to swagger UI in .NET core web api?如何在 .NET 核心 Web api 中将上传按钮添加到招摇 UI?
【发布时间】:2018-10-14 18:49:35
【问题描述】:

我有一个带有 swagger 的 ASP.net 核心 Web API(使用 swashbuckle)。

Web API 中的操作之一是文件上传操作:

[Produces("application/json")]
[Route("[controller]")]
public class FilesController : Controller
{
    [HttpPost]
    public void Post(IFormFile file)
    {
        ...
    }
}

当我在 swagger UI 中查找该操作时,它让我填写 IFormFile 的所有字段,这不是我想要测试我的 API 所做的。

那么如何向 Swagger UI 添加上传按钮?

【问题讨论】:

    标签: c# asp.net-core swagger swagger-ui asp.net-core-webapi


    【解决方案1】:

    首先添加一个使用多部分表单数据的操作过滤器。

    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);
        }
    

    现在您应该将您的操作添加到 _actionWithUpload 数组中! 请注意,我添加扩展只是为了有一个重构友好的过滤器。

    最后但同样重要的是,确保将操作过滤器添加到 swagger 的选项中。因此,将options.OperationFilter&lt;FileUploadOperation&gt;(); 添加到您的招摇选项中并完成。

    完整示例:

           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>();
            });
    

    【讨论】:

    • 很好的帮助方法来确定操作的名称。我看到很多人使用硬编码的名称,当名称更改时,总是很难弄清楚为什么有些东西不起作用。
    【解决方案2】:

    对于任何寻求开放式 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
        {
           
        }
    }
    

    使用 FileContentType 属性装饰控制器

    [HttpPost]
    [Route("PostFile")]
    [FileUploadOperation.FileContentType]
    public IActionResult PostFile(IFormFile uploadedFile)
    

    文件上传应在请求正文中生成,如下所示..

    【讨论】:

      【解决方案3】:

      除了@Nick 的回答,我还要对 AspNet core 2 进行 2 处更改。

      1] 更新 GetOperationId()

      现在所有 operationId 都包含 API 前缀以及后缀中的 Method。所以我在 ActionName 中静态添加了 API & Post。

      public static string GetOperationId<T>(string actionName) where T : ControllerBase => $"Api{GetControllerName<T>()}{actionName}Post";
      

      2] 只删除文件参数

      我不想删除该操作的所有参数,而只想删除文件参数。

      var fileParameter = operation.Parameters.FirstOrDefault(x => x.Name == "file" && x.In == "body");
      if (fileParameter != null)
      {
          operation.Parameters.Remove(fileParameter);
          ...
      }
      

      【讨论】:

        【解决方案4】:

        对于拥有多个端点需要上传文件并希望为其文件上传参数使用更通用/描述性/个人名称的任何人:

        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)
        {
        

        【讨论】:

          【解决方案5】:
          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; }
              }
          

          【讨论】:

          • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
          猜你喜欢
          • 2019-03-19
          • 1970-01-01
          • 2017-12-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多