如何让Swagger UI同时支持JSON请求体格式与文件上传?
解决Swagger UI同时展示JSON请求体与文件选择按钮的问题
针对你使用Swashbuckle.AspNetCore 6.5.0版本时,无法同时展示JSON编辑区域和文件选择按钮的问题,以下是具体实现方案:
1. 定义请求模型与控制器
首先创建包含JSON元数据和文件的请求模型,控制器方法通过[FromForm]接收混合请求:
// 请求模型 public class FileWithJsonRequest { // 存储JSON内容,后续会渲染为JSON编辑器 public string Metadata { get; set; } // 上传的文件 public IFormFile File { get; set; } } // 控制器方法 [HttpPost("upload-with-metadata")] public IActionResult UploadWithMetadata([FromForm] FileWithJsonRequest request) { // 将Metadata字符串解析为你的业务模型 var metadataModel = JsonSerializer.Deserialize<YourBusinessMetadataModel>(request.Metadata); // 处理上传的文件(示例逻辑) using var stream = new MemoryStream(); await request.File.CopyToAsync(stream); var fileBytes = stream.ToArray(); return Ok(new { Metadata = metadataModel, FileSize = fileBytes.Length }); }
2. 自定义Swagger过滤器
通过SchemaFilter和OperationFilter修改Swagger的API描述,将请求类型设置为multipart/form-data:
SchemaFilter(标记特殊模型)
public class FileWithJsonSchemaFilter : ISchemaFilter { public void Apply(OpenApiSchema schema, SchemaFilterContext context) { if (context.Type == typeof(FileWithJsonRequest)) { // 为模型添加自定义扩展标记,用于后续UI识别 schema.Extensions.Add("x-is-file-with-json", new OpenApiBoolean(true)); } } }
OperationFilter(配置请求内容类型)
public class FileWithJsonOperationFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) { var requestType = context.ApiDescription.ParameterDescriptions .FirstOrDefault(p => p.ParameterType == typeof(FileWithJsonRequest))?.ParameterType; if (requestType == typeof(FileWithJsonRequest)) { // 强制将请求内容类型设置为multipart/form-data operation.RequestBody = new OpenApiRequestBody { Content = new Dictionary<string, OpenApiMediaType> { ["multipart/form-data"] = new OpenApiMediaType { Schema = new OpenApiSchema { Type = "object", Properties = new Dictionary<string, OpenApiSchema> { ["Metadata"] = new OpenApiSchema { Type = "string", Format = "json" }, ["File"] = new OpenApiSchema { Type = "string", Format = "binary" } }, Required = new HashSet<string> { "Metadata", "File" } } } }, Required = true }; } } }
3. 配置Swagger服务
在Program.cs中注册过滤器:
builder.Services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "你的API名称", Version = "v1" }); // 添加自定义过滤器 c.SchemaFilter<FileWithJsonSchemaFilter>(); c.OperationFilter<FileWithJsonOperationFilter>(); });
4. 自定义Swagger UI渲染逻辑
通过注入自定义JS脚本,将Metadata文本框替换为JSON编辑器:
配置SwaggerUI注入脚本
app.UseSwaggerUI(c => { c.SwaggerEndpoint("/swagger/v1/swagger.json", "你的API V1"); // 注入自定义UI脚本 c.InjectJavascript("/swagger-ui/custom-file-json-script.js"); });
创建自定义JS文件
在wwwroot/swagger-ui/目录下创建custom-file-json-script.js,内容如下:
window.addEventListener('load', () => { // 监听SwaggerUI渲染变化 const observer = new MutationObserver((mutations) => { mutations.forEach(mutation => { if (mutation.addedNodes.length === 0) return; // 查找所有Metadata文本框 const metadataInputs = document.querySelectorAll('input[name="Metadata"]'); metadataInputs.forEach(input => { // 避免重复替换 if (input.nextElementSibling?.classList.contains('json-editor')) return; const parentDiv = input.parentElement; // 替换文本框为JSON编辑textarea parentDiv.innerHTML = ` <label for="Metadata">JSON元数据</label> <textarea id="Metadata" name="Metadata" class="json-editor" rows="8" placeholder="输入JSON内容..."></textarea> `; const textarea = parentDiv.querySelector('.json-editor'); if (!textarea) return; // 应用SwaggerUI风格样式 Object.assign(textarea.style, { width: '100%', padding: '10px', border: '1px solid #ccc', borderRadius: '4px', fontFamily: 'monospace' }); // 添加JSON格式化功能 textarea.addEventListener('blur', () => { try { const parsed = JSON.parse(textarea.value); textarea.value = JSON.stringify(parsed, null, 2); } catch (e) { // 无效JSON不处理 } }); }); }); }); // 监听SwaggerUI容器 observer.observe(document.getElementById('swagger-ui'), { childList: true, subtree: true }); });
关键说明
- 为什么不能用多Content-Type选择:SwaggerUI默认仅支持单Content-Type请求,而
multipart/form-data是唯一能同时传输文件和文本(JSON)的格式。 - 控制器需手动解析JSON:因为
Metadata是从表单中接收的字符串,需要自己反序列化为业务模型。
内容的提问来源于stack exchange,提问作者concaho
相关产品推荐
相关产品推荐

