You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# .NET API接收任意键值对:Swagger输入框显示异常问题

解决.NET API接收任意FormUrlEncoded键值对并让Swagger显示输入框的问题

问题背景

我使用C# .NET控制器和Swagger构建API,需要接收POST请求中任意数量的键值对(请求体格式为key1=val1&key2=val2&key3=val3a&key3=val3b这类表单编码格式),要求:

  1. 请求体能自动解析为IFormCollection
  2. Swagger UI提供对应的输入框用于提交数据

当前代码可正常处理请求,但Swagger未显示输入框:

[HttpPost]
[Consumes(MediaTypeNames.Application.FormUrlEncoded)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> SetKeys()
{
    var keyvals = await Request.Body.ToDictionary();  // 自定义Stream扩展方法
    if (keyvals.Count == 0)
        return BadRequest("No data received");

    // 数据库更新逻辑

    return NoContent(); //204
}

尝试过以下写法均无法满足需求:

public async Task<IActionResult> SetKeys([FromForm] string content)
public async Task<IActionResult> SetKeys([FromBody] string content)
public async Task<IActionResult> SetKeys([FromForm] IFormCollection content)
public async Task<IActionResult> SetKeys([FromBody] IFormCollection content)
public async Task<IActionResult> SetKeys([FromBody] List<KeyValuePair<string, string>> content)

若将Consumes设为text/plain,Swagger会显示输入框,但需手动解析键值对,且失去Content-Type校验:

[HttpPost]
[Consumes(MediaTypeNames.Text.Plain)]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, MediaTypeNames.Application.FormUrlEncoded)]
public async Task<IActionResult> SetKeys([FromBody] string content)
{
    var keyvals = await content.ToDictionary();  // 自定义String扩展方法
    if (keyvals.Count == 0)
        return BadRequest("No data received");

    // 数据库更新逻辑

    return NoContent(); //204
}

自定义InputFormatter后,Swagger虽能选择application/x-www-form-urlencoded,但会将内容按字符拆分(如key1=val1被拆为0=k&1=e&2=y...):

using System.Net.Mime;
using Microsoft.AspNetCore.Mvc.Formatters;

namespace RequestRouter.Utilities;

public class RawBodyInputFormatter : InputFormatter
{
    public RawBodyInputFormatter()
    {
        this.SupportedMediaTypes.Add(MediaTypeNames.Application.FormUrlEncoded);
        //this.SupportedMediaTypes.Add(MediaTypeNames.Text.Plain);
    }

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var request = context.HttpContext.Request;
        using var reader = new StreamReader(request.Body);
        var content = await reader.ReadToEndAsync();
        return await InputFormatterResult.SuccessAsync(content);
    }

    protected override bool CanReadType(Type type)
    {
        return type == typeof(string);
    }
}

解决方案

要同时满足自动解析和Swagger输入框需求,需完成以下两步:

1. 修正控制器方法参数

使用[FromForm] IFormCollection作为参数,保留Consumes特性指定表单编码格式:

[HttpPost]
[Consumes(MediaTypeNames.Application.FormUrlEncoded)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> SetKeys([FromForm] IFormCollection formData)
{
    if (formData.Count == 0)
        return BadRequest("未接收到数据");

    // 处理重复键示例:获取key3的所有值
    var key3Values = formData.GetValues("key3");

    // 数据库更新逻辑

    return NoContent();
}

2. 配置Swagger支持动态表单参数

默认Swagger对IFormCollection生成固定表单,需添加自定义操作过滤器实现任意键值对输入:

创建操作过滤器

using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

public class FormCollectionOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        var formParam = context.ApiDescription.ParameterDescriptions
            .FirstOrDefault(p => p.Type == typeof(IFormCollection));

        if (formParam != null)
        {
            // 移除自动生成的无效参数
            operation.Parameters.Clear();
            
            // 添加支持任意键值对的表单请求体
            operation.RequestBody = new OpenApiRequestBody
            {
                Content = new Dictionary<string, OpenApiMediaType>
                {
                    {
                        MediaTypeNames.Application.FormUrlEncoded,
                        new OpenApiMediaType
                        {
                            Schema = new OpenApiSchema
                            {
                                Type = "object",
                                AdditionalProperties = new OpenApiSchema
                                {
                                    Type = "string"
                                }
                            }
                        }
                    }
                }
            };
        }
    }
}

注册过滤器到Swagger

在Program.cs中添加配置:

builder.Services.AddSwaggerGen(c =>
{
    c.OperationFilter<FormCollectionOperationFilter>();
    // 其他Swagger配置(如文档标题、版本等)
});

效果说明

  • Swagger UI会显示可动态添加键值对的表单区域,符合application/x-www-form-urlencoded格式要求
  • IFormCollection自动解析请求体,重复键可通过GetValues方法获取所有值
  • 保留Content-Type校验,仅当请求头为application/x-www-form-urlencoded时才会被正确处理

内容的提问来源于stack exchange,提问作者Christian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 11:14:53