I'm trying to send a "Content-Type" header: "application/x-www-form-urlencoded" to a minimal api and I get the following error:
Microsoft.AspNetCore.Http.BadHttpRequestException: Expected a supported JSON media type but got "application/x-www-form-urlencoded".
Here's the endpoint:
app.MapPost("/api/formulario/imovel-web/cadastrar",
async (LeadRequest request, ILeadService service, ILogService logService, ICorretorMrvRepository corretorMrvRepository) =>
{
request.Origem = "imovelweb";
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");
This is the same problem as .NET 6 Minimal API and multipart/form-data . The answer explains it's not currently possible to bind form values in minimal APIs. However, there are a couple of work-arounds which are detailed in the answer:
BindAsync
HttpContext
Utilizing MVC Core model binding
However, there is another method where we can leverage some of the model-binding mechanisms that already exist in MVC, in your minimal API. The below is inspired by Abuhakmeh's page Using ASP.NET Core MVC Value Providers With Minimal APIs .
First, add a BindAsync
method to your model ( LeadRequest
) which generates an instance from the request context:
public class LeadRequest
{
// class properties
...
public static async ValueTask<LeadRequest?> BindAsync(HttpContext httpContext, ParameterInfo parameter)
{
return await httpContext.BindFromForm<LeadRequest>();
}
}
where I've assumed LeadRequest
is a class but this will work with records too.
Now, you need to add an extension method for BindFromForm<T>()
. This utilizes model-binding available in Asp.Net MVC Core.
using System.Globalization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace YourProjectNamespace;
public static class BindingExtensions
{
public static async Task<T?> BindFromForm<T>(this HttpContext httpContext)
{
var serviceProvider = httpContext.RequestServices;
var factory = serviceProvider.GetRequiredService<IModelBinderFactory>();
var metadataProvider = serviceProvider.GetRequiredService<IModelMetadataProvider>();
var metadata = metadataProvider.GetMetadataForType(typeof(T));
var modelBinder = factory.CreateBinder(new() {
Metadata = metadata
});
var context = new DefaultModelBindingContext
{
ModelMetadata = metadata,
ModelName = string.Empty,
ValueProvider = new FormValueProvider(
BindingSource.Form,
httpContext.Request.Form,
CultureInfo.InvariantCulture
),
ActionContext = new ActionContext(
httpContext,
new RouteData(),
new ActionDescriptor()),
ModelState = new ModelStateDictionary()
};
await modelBinder.BindModelAsync(context);
return (T?) context.Result.Model;
}
}
Finally, you need to register MVC Core with your application services:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMvcCore();
...
app.MapPost("/api/formulario/imovel-web/cadastrar",
async (LeadRequest request, ILeadService service, ILogService logService, ICorretorMrvRepository corretorMrvRepository) =>
{
...
}
This will bind the request data to your model. Note: there's no need to use Accepts<LeadRequest>("application/x-www-form-urlencoded")
when using this method.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.