Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
420 views
in Technique[技术] by (71.8m points)

c# - .Net Core Api cannot access original value on model validation failures

I cannot access the original value that didn't pass the model validation. I would suspect AttemptedValue and/or RawValue in ModelStateEntry to contain the original value, however both properties are null.

For clarification, I wrote a minimalistic api, to showcase the issue.

The model to validate:

public class User
{
    [EmailAddress]
    public string Email { get; set; }
}

The controller:

[ApiController]
[Route("test")]
public class TestController : ControllerBase
{
    [HttpPost]
    [ValidationFilter()]
    public string Test([FromBody] User user)
    {
        return user.Email;
    }
}

The validation filter:

public class ValidationFilterAttribute : ActionFilterAttribute, IOrderedFilter
{
    public int Order { get; } = int.MinValue;

    override public void OnActionExecuting(ActionExecutingContext context)
    {
        if (!context.ModelState.IsValid)
        {
            ModelStateEntry entry = context.ModelState.ElementAt(0).Value;
            var attemptedVal = entry.AttemptedValue;
            var rawVal = entry.RawValue;
            context.Result = new OkObjectResult(rawVal);
        }
    }
}

When I call the test method with this model:

{
    "email": "No email here ;)"
}

The ValidationFilterAttribute code is called as expected, however the ModelStateEntry does not contain the original value. Both AttemptedValue and RawValue are null:

Visual Studio debugging screenshot


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

As far as I know, for model binding, the filter will calls context.ModelState.SetModelValue to set value for RawValue and AttemptedValue.

But the SystemTextJsonInputFormatter doesn't set it to solve this issue, I suggest you could try to build custom extension method and try again.

More details, you could refer to below codes:

Create a new ModelStateJsonInputFormatter class:

public class ModelStateJsonInputFormatter : SystemTextJsonInputFormatter
{
    public ModelStateJsonInputFormatter(ILogger<ModelStateJsonInputFormatter> logger, JsonOptions options) : 
        base(options ,logger)
    {
    }
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        var result = await base.ReadRequestBodyAsync(context);
        foreach (var property in context.ModelType.GetProperties())
        {
            var propValue = property.GetValue(result.Model, null);
            var propAttemptValue = property.GetValue(result.Model, null)?.ToString();
            context.ModelState.SetModelValue(property.Name, propValue, propAttemptValue);
        }
        return result;
    }
}

Reigster it in startup.cs:

            services.AddControllersWithViews(options => {
                var serviceProvider = services.BuildServiceProvider();
                var modelStateJsonInputFormatter = new ModelStateJsonInputFormatter(
            serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<ModelStateJsonInputFormatter>(),
serviceProvider.GetRequiredService<IOptions<JsonOptions>>().Value);
                options.InputFormatters.Insert(0, modelStateJsonInputFormatter);
                });

Result:

enter image description here


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...