1

I have an ASP.net Core 3.1 web api project. In one of my controllers, I have a Post method that accepts a request object that contains an array of objects. I have done this same thing in ASP.Net using .Net Framework and in ASP.Net Core 2.1 with no problem. However, in 3.1 The array is not getting bound when calling the api method.

 [HttpPost(), Route("{id}/Answers")]
    [ProducesResponseType(typeof(QuestionaireAnswerModel), StatusCodes.Status201Created)]
    public async Task<IActionResult> PostAnswers(int id, QuestionaireAnswerRequestModel request) {
        try
        {
            if (ModelState.IsValid)
            {
                if (request.Answers == null || request.Answers.Count < 1) 
                    return BadRequest("Answers are required.");
                var result = await _service.CreateQuestionaireAnswersAsync(id, request);
                return Created($"https://blah/api/{result.Id}", result);
            }
            else { return BadRequest(ModelState); }
        }
        catch (Exception ex)
        {
            var message = $"An error occurred posting answers for Questionnaire. Questionnaire Id: {id}, UserName: {request.UserName}";
            _logger.LogError(ex, message);
            return StatusCode(StatusCodes.Status500InternalServerError, message);
        }
    }

c# models

public class QuestionaireAnswerRequestModel
    {
        public string UserName { get; set; }
        public IEnumerable<QuestionAnswerRequestModel> Answers;
    }

public class QuestionAnswerRequestModel
    {
        public int QuestionId { get; set; }
        public bool Answer { get; set; }
    }

Sample request used in postman to test {

"userName": "testuser",
"answers": [{
    "questionId": 1,
    "answer": "false"
}]

}

Trying to debug and the answers list is always null. I have tried using an array and List type instead of IEnumerable with no luck. I know they changed the serializer in 3.1 but not sure why an array is not able to serialize? Does Anyone know the answer? here is my startup.cs

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {            
        var connection = Configuration.GetConnectionString("HealthScreeningConnection");
        services.AddEntityFrameworkSqlServer()
            .AddEntityFrameworkProxies()
            .AddDbContextPool<HealthScreeningContext>((serviceProvider, options) => {
                options.UseSqlServer(connection).UseInternalServiceProvider(serviceProvider);
                options.UseLazyLoadingProxies();
            });
        services.AddScoped<IQuestionareService, QuestionaireService>();
        services.AddScoped<IAccountService, AccountService>();
        services.AddControllers();
        services.AddCors(opts => {
            opts.AddPolicy("CorsPolicy", builder => builder.AllowAnyOrigin()
                .AllowAnyMethod()
                .AllowAnyHeader());
        });
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        var nlogFilePath = "nlog.config";
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
            nlogFilePath = $"nlog.{env.EnvironmentName}.config";
        }
        NLog.LogManager.LoadConfiguration(nlogFilePath);

        app.UseHttpsRedirection();

        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthorization();
        app.UseCors("CorsPolicy");

        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();

        });
    }
}
6
  • Have you tried to put [FromBody] in front of QuestionaireAnswerRequestModel in controller action? Commented May 27, 2020 at 20:17
  • 1
    Yes, I have tried [FromBody] with no luck. I also took the id out of the uri and added it to the request object. All but the array gets passed through. I have not tried changing the serializer to Newtonsoft Json.Net yet as it should not be the problem...I would not think. Commented May 27, 2020 at 20:21
  • Does it have something to do with camel casing and pascal casing? Commented May 27, 2020 at 20:24
  • It is quite frustrating that something as simple as passing an array of objects needs to be like pulling teeth. This all worked in previous versions of .net and now I am at a standstill trying to figure out something as stupid as this. Commented May 27, 2020 at 20:26
  • Michael - I did change the casing on the request and in the postman request as well with no luck. Definitely something related to core 3.1, but cannot put my finger on it. Spent better part of a day trying to find the solution. Commented May 27, 2020 at 20:27

1 Answer 1

1

I think that you need to make QuestionaireAnswerRequestModel field Answers a property eg. add { get; set; } to it:

public class QuestionaireAnswerRequestModel
{
    public string UserName { get; set; }
    public IEnumerable<QuestionAnswerRequestModel> Answers { get; set; }
}

From MSDN:

A complex type must have a public default constructor and public writable properties to bind. When model binding occurs, the class is instantiated using the public default constructor.

Surce: https://learn.microsoft.com/en-us/aspnet/core/mvc/models/model-binding?view=aspnetcore-3.1

Sign up to request clarification or add additional context in comments.

1 Comment

looking back....it was a simple stupid mistake....lol

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.