2022-07-20

Failed to construct 'Request': Request with GET/HEAD method cannot have body

please, I need your help. I'm learning to develop APIs, so I created an Asp.Net core 6 Api using VS2022 version 17.26. when I tried to test my API through Swager I got an error message "Failed to construct 'Request': Request with GET/HEAD method cannot have body".

I tried postman and I got another error message:

{
    "errors": {
        "": [
            "A non-empty request body is required."
        ],
        "request": [
            "The request field is required."
        ]
    },
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-8dc5d67e871061e09d8d264769b31a9f-52123737ebab3abc-00"

I struggled for the last 5 days trying to figure out the issue with no luck.

This is my program file code:

var CORS_POLICY = "CorsPolicy";

var config = new ConfigurationBuilder()
    .AddJsonFile("appsettings.json")
    .Build();
Log.Logger = new LoggerConfiguration()
    .ReadFrom.Configuration(config)
    .MinimumLevel.Debug()
    .WriteTo.Console()
    .WriteTo.File("Logs/log-.txt", rollingInterval: RollingInterval.Day)
    .CreateLogger();

var builder = WebApplication.CreateBuilder(args);

builder.Host.UseSerilog();

// Add services to the container.
builder.Services.AddControllers(options =>
{
    options.ReturnHttpNotAcceptable = true;
}).AddNewtonsoftJson()
.AddXmlDataContractSerializerFormatters();

// add DbContext
builder.Services.AddDbContext<AppDbContext>(options =>
    options
    .UseSqlServer(builder.Configuration.GetConnectionString("DataConnectionString"),
    sqlServerOptionsAction: sqloptions =>
    {
        sqloptions.EnableRetryOnFailure(
            maxRetryCount: 10,
            maxRetryDelay: TimeSpan.FromSeconds(5),
            errorNumbersToAdd: null);
    }));

builder.Services.AddControllers();

// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

// Call UseServiceProviderFactory on the Host sub property 
builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
{
    containerBuilder.RegisterMediatR(typeof(Program).Assembly);
    containerBuilder.RegisterModule<ApplicationModule>();
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}


app.UseCors(CORS_POLICY);

app.UseHttpsRedirection();

// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.), 
// specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
    c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API");

});

app.MapControllers();
app.Run();

I could not reach the endpoint.

I want to mention that I’m using (Ardalis.ApiEndpoints) package and I have created two endpoints Create and List. So, When I run the API and test Create EndPoint it works fine but when I run the List EndPoint I got the error. I think it could be a routing issue not sure about that.

Folder: \Endpoints \City

Create EndPoint:

namespace EA.Api.CityEndpoints
{
    public class Create : EndpointBaseAsync
        .WithRequest<CreateCityCommand>
        .WithActionResult<CreateCityCommandResponse>
    {
        private readonly IMediator _mediator;

        public Create(IMediator mediator)
        {
            _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
        }

        [HttpPost(CreateCityCommand.Route)] //"api/cities"
        [SwaggerOperation(
            Summary = "Creates a new City",
            Description = "Creates a new City",
            OperationId = "cities.Create",
            Tags = new[] { "CityEndpoints" })
        ]
        public override async Task<ActionResult<CreateCityCommandResponse>>
            HandleAsync([FromQuery] CreateCityCommand request, CancellationToken cancellationToken = default)
        {
            var response = await _mediator.Send(request);
            return Ok(response);
        }
    }
}

List EndPoint:

namespace EA.Api.CityEndpoints
{
    public class List : EndpointBaseAsync
        .WithRequest<GetCitiesListQuery>
        .WithActionResult<List<CitiesListVm>>
    {

        private readonly IMediator _mediator;

        public List(IMediator mediator)
        {
            _mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
        }

        [HttpGet(GetCitiesListQuery.Route)] //"api/cities"
        [SwaggerOperation(
            Summary = "List Cities",
            Description = "List Cities",
            OperationId = "cities.List",
            Tags = new[] { "CityEndpoints" })
        ]
        public override async Task<ActionResult<List<CitiesListVm>>> HandleAsync(GetCitiesListQuery request, 
            CancellationToken cancellationToken = default)
        {
            var dtos = await _mediator.Send(new GetCitiesListQuery());
            return Ok(dtos);
        }
    }
}

I figure out the cause of the problem but still tried to solve it. The problem occurs due to the use of MediatR. So when I removed the GetCitiesListQuery from the List Endpoint and make it Without a Request and Without a response, the endpoint work fine. Putting them back I got the same issue.

I don’t know why but let me explain how I’m using it. Maybe someone can help and direct me to what I’m doing wrong here.

My solution contains the following apps:

Asp.net Core Api

Packages used for Api 1.MediatR 2.Ardalis.ApiEndpoints 3.Autofac.Extensions.DependencyInjection

Class Library EA.Application which contain my Commands and Handlers classes for Request\Response messages.

Packages used for Class Library 1.Autofac 2.MediatR.Extensions.Autofac.DependencyInjecti

I have a class in my Class Libaray with the name ApplicationModule contains all of the command and Handler classes which I registered during app startup in program file.

public class ApplicationModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        builder.RegisterType<GetCitiesListQuery>().AsImplementedInterfaces();
        builder.RegisterType<GetCitiesListQueryHandler>().AsImplementedInterfaces();
    }

} 


public class GetCitiesListQuery : IRequest<List<CitiesListVm>>
{
    public const string Route = "/cities";
}

public class GetCitiesListQueryHandler : IRequestHandler<GetCitiesListQuery, List<CitiesListVm>>
{

    public async Task<List<CitiesListVm>> Handle(GetCitiesListQuery request, CancellationToken cancellationToken)
    {
        var allCities = (await _cityRepository.ListAllAsync()).OrderBy(x => x.CityNameEng);
        return _mapper.Map<List<CitiesListVm>>(allCities);
    }
}


public class CitiesListVm
{
    public int CityId { get; set; }
    public string CityNameEng { get; set; } = string.Empty;
    public string CityNameArb { get; set; } = string.Empty;
}

Please, if someone knows the issue let me know.



No comments:

Post a Comment