.NET 6 Minimal API接收x-www-form-urlencoded请求报媒体类型错误
问题根因
.Accepts<LeadRequest>("application/x-www-form-urlencoded") 配置仅用于生成Swagger/OpenAPI接口文档的元数据,不会修改Minimal API运行时的参数绑定逻辑。
.NET 6 Minimal API默认对自定义复杂类型参数(如你的LeadRequest)仅支持从application/json类型的请求体做反序列化绑定,遇到其他Content-Type的请求就会抛出你看到的Expected a supported JSON media type but got "application/x-www-form-urlencoded"错误。
解决方案
你可以根据需求选择以下两种方案:
方案1:无额外依赖,手动从Form表单绑定模型
不需要引入任何第三方包,直接将端点参数中的LeadRequest替换为HttpRequest对象,通过Request.Form手动读取表单字段映射到模型即可,原有业务逻辑无需修改。
修改后的端点示例:
app.MapPost("/api/formulario/imovel-web/cadastrar", async (HttpRequest httpRequest, ILeadService service, ILogService logService, ICorretorMrvRepository corretorMrvRepository) => { // 先读取表单数据 var form = await httpRequest.ReadFormAsync(); // 手动映射字段到LeadRequest var request = new LeadRequest { Origem = "imovelweb", Token = form[nameof(LeadRequest.Token)].ToString(), txtDdd = form[nameof(LeadRequest.txtDdd)].ToString(), txtTelefone = form[nameof(LeadRequest.txtTelefone)].ToString() // 按照LeadRequest的属性,逐一对应表单字段完成映射即可 }; // 以下为原有业务逻辑,无需修改 try { if (string.IsNullOrEmpty(request.Token)) { logService.Error("leads-api", "imovel-web: Acesso não autorizado (Token não encontrado).", Environment.StackTrace, request: request.Serialize()); return Result.ResponseFailed("Acesso não autorizado (Token não encontrado)."); } else { var idParceiro = await corretorMrvRepository.ObterIdParceiro("imovelweb"); var origem = string.Empty; if (idParceiro != Guid.Empty) { var jwt = request.Token; var handler = new JwtSecurityTokenHandler(); var data = handler.ReadJwtToken(request.Token); var autorizado = data.Claims.Any(x => x.Type.Equals("origin") && x.Value.Equals("imovelweb")) && data.Claims.Any(x => x.Type.Equals("key") && x.Value.Equals(idParceiro.ToString())); if (!autorizado) { logService.Error("leads-api", "imovel-web: Acesso não autorizado.", Environment.StackTrace, request: request.Serialize()); return Result.ResponseFailed("Acesso não autorizado (Dados do token não encontrados)."); } request.Origem = await corretorMrvRepository.ValidaParceiroLead(idParceiro); } else { logService.Error("leads-api", "imovel-web: Acesso não autorizado (Id do parceiro não encontrado).", Environment.StackTrace, request: request.Serialize()); return Result.ResponseFailed("Acesso não autorizado (Id do parceiro não encontrado)."); } if (!string.IsNullOrEmpty(request.txtDdd) && !string.IsNullOrEmpty(request.txtTelefone)) request.txtTelefone = $"{request.txtDdd}{request.txtTelefone}".FormatPhone(false); var responseResult = await service.Cadastrar(request); return Result.Response<ResponseResult>(responseResult, responseResult.Success); } } catch (Exception e) { logService.Error("leads-api", new ExceptionForLog(e.Message, e)); return Result.ResponseFailed(e.Message); } }) .Accepts<LeadRequest>("application/x-www-form-urlencoded") .Produces<ResponseResult>() .ProducesValidationProblem() .WithTags("Imóvel Web") .WithName("CadastrarImovelWeb");
方案2:引入扩展包实现自动表单绑定
如果不想手动写字段映射逻辑,可以安装支持Minimal API表单绑定的扩展包,给参数加上[FromForm]特性即可实现自动绑定,无需修改业务代码。
- 安装Nuget包:
MinimalApis.Extensions(.NET 6环境下兼容的Minimal API扩展包,支持复杂类型FromForm绑定) - 给
LeadRequest参数添加[FromForm]特性,示例:
app.MapPost("/api/formulario/imovel-web/cadastrar", async ([FromForm] LeadRequest request, ILeadService service, ILogService logService, ICorretorMrvRepository corretorMrvRepository) => { // 原有所有业务逻辑无需任何修改 request.Origem = "imovelweb"; // ... 保留原来的所有业务代码即可 }) // 原有链式配置保留即可 .Accepts<LeadRequest>("application/x-www-form-urlencoded") .Produces<ResponseResult>() .ProducesValidationProblem() .WithTags("Imóvel Web") .WithName("CadastrarImovelWeb");
注意:.NET 7及以上版本的Minimal API已经原生支持
[FromForm]绑定复杂类型,不需要额外安装扩展包,直接加特性即可。
内容的提问来源于stack exchange,提问作者Rodrigo De Freitas Oliveira
相关产品推荐
相关产品推荐

