Search code examples
microservicesasp.net-core-webapiasp.net-core-8

ASP.NET Core 8.0 Web API : endpoint accepting only dynamic values as parameters


I'm currently working on an API that should be adding a client in the database and basically my controller looks like this:

[ApiController]
[Route("api/[controller]")]
[Authorize()]
public class ClientController : ControllerBase
{
    private readonly IClientService ClientService;

    public ClientController(IClientService clientService)
    {
        this.ClientService = clientService;
    }

    [HttpPost("AddClient")]
    [Consumes("application/json")]
    public async Task<IActionResult> AddClient([FromBody]AddNewClientDTO client)
    {
        if (ModelState.IsValid)
        {
            await this.ClientService.AddClient(client);
        }

        return Ok();
    }
}

However this code is not working. I have tried multiple things and the only time it's working is when I change the AddNewClientDTO to object or dynamic.

This API is being called via the Web interface which is a ASP.NET application. When the I fill out the form, the controller is making the API call which is being handled by API Gateway. For the gateway, I'm using Ocelot.

I also have authentication with Auth0, but for it I know it's working as I'm not having issues with the GET for example. When I was testing to make the call via Postman it's working and the client is being added in the database.

Here is the code that is calling the API:

var jsonModel = JsonConvert.SerializeObject(model);

using (HttpClient httpClient = new HttpClient())
{
    httpClient.DefaultRequestHeaders.Accept.Clear();
    httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
    httpClient.DefaultRequestHeaders.Add("authorization", $"Bearer {authenticationProviderKey}");
    httpClient.DefaultRequestHeaders.Add("client", jsonModel);
    StringContent kpcontent = new StringContent(jsonModel, Encoding.UTF8, "application/json");

    var kpresponse = await httpClient.PostAsync("https://localhost:7022/api/Client/AddClient", kpcontent);
}

When I'm using Postman to manually pass the client parameters, I'm getting the AddNewClientDTO object when calling the API, but when using the web app, it's returning "Error 400 - Bad Request" and the only way I can make it working is when I use dynamic or object.

How can I make the AddClient method to receive the AddNewClientDTO object instead of dynamic? Am I doing something wrong when calling the API?


Solution

  • I assume this is because you have a non nullable property defined in your model that you're not sending with your request.

    public class Test 
    {   
        // All defined as non nullable and must be provided when calling API
        public string Value1 { get; set; }
        public object Value2 { get; set; }
        public int Value3 { get; set; }
    }
    
    // What I think youre doing is creating your object without a required property ie. (missing Value1) 
    var model = new Test()
    {
        // We are NOT defining "Value1"
        Value2 = new object(),
        Value3 = 4
    }
    
    var jsonModel = JsonConvert.SerializeObject(model);
    
    using (HttpClient httpClient = new HttpClient())
    {
        httpClient.DefaultRequestHeaders.Accept.Clear();
        httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
        httpClient.DefaultRequestHeaders.Add("authorization", $"Bearer {authenticationProviderKey}");
        httpClient.DefaultRequestHeaders.Add("client", jsonModel);
        StringContent kpcontent = new StringContent(jsonModel, Encoding.UTF8, "application/json");
    
        var kpresponse = await httpClient.PostAsync("https://localhost:7022/api/Client/AddClient", kpcontent);
    }
    

    The above will result in a bad request in kpresponse. If a property can be null (not provided) then define it as such using the "?" after the data type ie.

    public class Test 
    {    
        public string? Value1 { get; set; }
        public object Value2 { get; set; }
        public int Value3 { get; set; }
    }
    

    Note: You can disable this nullable check by the model validator which is on by default but I wouldn't