skip to Main Content

In my ASP.NET Core Web API, when I call a method that is a HttpPost and receives a DTO object in FromBody, and sends extra fields that are not in SourceUrlDto, I fail during binding on the code side.

How can I access this data on name of basicDto?

public class SourceUrlDto
{
    public string SourceApiUrl { get; set; }
    public string SourceApiMethodType { get; set; }
    public string AuthenticationType { get; set; }
}

public class BasicSourceUrlDto : SourceUrlDto
{
    public string Username { get; set; }
    public string Password { get; set; }
}

[HttpPost]
[Route("GetUrlData")]
public async Task<IActionResult> GetUrlData([FromBody] SourceUrlDto sourceUrlDto)
{
    if (sourceUrlDto is BasicSourceUrlDto basicDto)
    {   
        // ...
    }

    return Ok();
}

2

Answers


  1. Change your getUrlData method signature to the below:

    public async Task<IActionResult> GetUrlData([FromBody] JObject json)
    

    Then you can access additional params like below:

    using Newtonsoft.Json.Linq;
    
    [HttpPost]
    [Route("GetUrlData")]
    public async Task<IActionResult> GetUrlData([FromBody] JObject json)
    {
        string sourceApiUrl = json["SourceApiUrl"]?.ToString();
        string sourceApiMethodType = json["SourceApiMethodType"]?.ToString();
        string authenticationType = json["AuthenticationType"]?.ToString();
    
        string username = json["Username"]?.ToString();
        string password = json["Password"]?.ToString();
    
        if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(password))
        {
            BasicSourceUrlDto basicDto = new BasicSourceUrlDto
            {
                SourceApiUrl = sourceApiUrl,
                SourceApiMethodType = sourceApiMethodType,
                AuthenticationType = authenticationType,
                Username = username,
                Password = password
            };
    
    
        }
        else
        {
            SourceUrlDto sourceDto = new SourceUrlDto
            {
                SourceApiUrl = sourceApiUrl,
                SourceApiMethodType = sourceApiMethodType,
                AuthenticationType = authenticationType
            };
    
            // Use SourceUrlDtoas needed ...
        }
    
        return Ok();
    }
    

    Call it via from your client like below:

    fetch('/GetUrlData', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify({
            SourceApiUrl: 'your-url',
            SourceApiMethodType: 'GET',
            AuthenticationType: 'Basic',
            Username: 'your-username',
            Password: 'your-password'
        })
    });
    
    Login or Signup to reply.
  2. In my ASP.NET Core Web API, when I call a method that is a HttpPost
    and receives a DTO object in FromBody, and sends extra fields that are
    not in SourceUrlDto, I fail during binding on the code side.

    Your request has been failed because, the sourceUrlDto parameter is explicitly typed as SourceUrlDto, so the model binder will only look for properties defined in SourceUrlDto.

    Another reason is that, JSON properties like Username and Password are ignored during model binding because they are not part of SourceUrlDto.

    Also, the cast sourceUrlDto is BasicSourceUrlDto will always fail because the binder doesn’t create an instance of BasicSourceUrlDto even if the JSON contains fields for Username and Password.

    In order to fix that, you either can use custom model binder or manually map the model using reflection.

    How can I access this data on name of basicDto?

    Depending on your scenario, I would personally prefer manual dictionary map of DTO. You can use reflection. Below is a simple example using reflection to manually map properties.

    First of all, I would use Dictionary<string, object> json as [FromBody] request parameter.

    After that, I would map the SourceUrlDto from the json I would recieve from request.

    Finally, would extract the value using reflaction.

    Let’s have a look in pactice:

    [HttpPost]
    [Route("GetUrlData")]
    public async Task<IActionResult> GetUrlData([FromBody] Dictionary<string, object> json)
    {
        
        var sourceUrlDto = new SourceUrlDto
        {
            SourceApiUrl = json.ContainsKey(nameof(SourceUrlDto.SourceApiUrl)) ? json[nameof(SourceUrlDto.SourceApiUrl)].ToString() : null,
            SourceApiMethodType = json.ContainsKey(nameof(SourceUrlDto.SourceApiMethodType)) ? json[nameof(SourceUrlDto.SourceApiMethodType)].ToString() : null,
            AuthenticationType = json.ContainsKey(nameof(SourceUrlDto.AuthenticationType)) ? json[nameof(SourceUrlDto.AuthenticationType)].ToString() : null
        };
    
       
        var additionalProperties = json
            .Where(p => !typeof(SourceUrlDto).GetProperties().Any(pi => pi.Name.Equals(p.Key, StringComparison.OrdinalIgnoreCase)))
            .ToDictionary(p => p.Key, p => p.Value.ToString());
    
        if (additionalProperties.Any())
        {
            foreach (var prop in additionalProperties)
            {
                Console.WriteLine($"Additional Property - {prop.Key}: {prop.Value}");
            }
        }
    
        
        var basicDto = new BasicSourceUrlDto
        {
            SourceApiUrl = sourceUrlDto.SourceApiUrl,
            SourceApiMethodType = sourceUrlDto.SourceApiMethodType,
            AuthenticationType = sourceUrlDto.AuthenticationType
        };
    
        if (additionalProperties.ContainsKey(nameof(BasicSourceUrlDto.Username)))
        {
            basicDto.Username = additionalProperties[nameof(BasicSourceUrlDto.Username)];
        }
    
        if (additionalProperties.ContainsKey(nameof(BasicSourceUrlDto.Password)))
        {
            basicDto.Password = additionalProperties[nameof(BasicSourceUrlDto.Password)];
        }
    
    
        return Ok(basicDto);
    }
    

    Output:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    Note: Keep in mind, this one of the approach using dictionary and reflaction considering your scenario. You could also try using custom model binder. However, it depends on programmer preference and sense of data structure.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search