简体   繁体   中英

ASP.NET CORE Model binding: Why the getter of DTO is get called by runtime before the value is set?

I am trying to use the getter to valid the property of DTO, If the property is invalid the exception is thrown, the sample code as follow:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{

    private readonly ILogger<WeatherForecastController> _logger;

    public WeatherForecastController(ILogger<WeatherForecastController> logger)
    {
        _logger = logger;
    }


    [HttpPost("test")]
    public IActionResult Post([FromBody] Foo f)
    {
        return Ok(f.Bar.Count == 1);
    }
}

public class Foo
{
    private List<string> _bar;
    public List<string> Bar
    {
        get
        {
            if (_bar == null || _bar.Count == 0) throw new System.Exception("at lease one element is required!");
            return _bar;
        }
        set
        {
            _bar = value;
        }
    }
}

use the curl to debug the api

curl --location --request POST 'http://localhost:5000/WeatherForecast/test2' --header 'Content-Type: application/json' --data-raw '{
    "Baz":"HelloWorld"
}'

during the API debugging, I observed that the setter is get called with an empty value of list, even if the corresponding input has value, and then was the getter. As a result, the getter thrown an exception due to the empty list.

It seem that the model binding behavior has some differences between enumerable type and non-enumerable type ( if I changed the Bar from List<string> into string , I would get the value in the setter);

Question:

  1. Is there any way to configure asp.net core to bind the property of enumerable type in the way of non-enumerable type, I would like to do the validation in the getter instead of other place

In your request model Foo , you assert that Bar is going to be a collection of strings (eg, an array). However, in your curl request, you are setting Bar to a single string instead of an array containing one string.

Instead, you should use curl --location --request POST 'http://localhost:5000/WeatherForecast/test2' --header 'Content-Type: application/json' --data-raw '{ "Bar": ["HelloWorld"] }' .

If you want to accept either a string or an array for Bar , then you cannot use your DTO class with [FromBody] . Instead, your will have to write your own logic to determine the type of data in the request body and then manually parse the JSON data into the appropriate model.

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM