Web API model validation and default values

2019-05-08 15:44发布

This is the spiritual successor to my previous question Web API attribute routing and validation - possible?, which I think was too general to answer. Most of those issues are solved, but the default value question remains.

Basically I have solved many pieces of the puzzle. I have this:

[HttpGet]
[Route("test/{id}"]
public IHttpActionResult RunTest([FromUri]TestRequest request)
{
    if (!ModelState.IsValid) return BadRequest(ModelState);
    return Ok();
}

My TestRequest class:

public class TestRequest
{
    public string id { get; set; }

    [DefaultValue("SomethingDefault")]
    public string something { get; set; }
}

The problem is that if no parameter is in the query string for something, the model is "valid" and yet something is null.

If I specify a blank value for something (i.e. GET test/123?something=), then the default value comes into play, and the model is valid again.

Why is this? How can I get a default value into my model here? As a bonus, why is it when a parameter is not specified, the default value is not used, but when a blank string is explicitly specific, the default value is used?

(I've been trawling through the ASP.NET stack source code and am knee-deep in model binders and binding contexts. But my best guess can't be right - it looks like the DefaultValueAttribute is used only if the parameter value is null. But that's not the case here)

2条回答
太酷不给撩
2楼-- · 2019-05-08 16:12

Specifying the default in the constructor works for when no parameter is specified at all, but when a blank string is specified, null is put into the field instead.

As such, adding [DefaultValue("")] actually worked the best - when a blank string was specified, a blank string was passed in. Then the constructor can specify default values for when the parameter is missing.

To get around this, I've created PreserveBlankStringAttribute, derives from DefaultValueAttribute which is equivalent to [DefaultValue("")].

I would very much welcome a better answer than this, please.

查看更多
SAY GOODBYE
3楼-- · 2019-05-08 16:26

You need to initialize the default value in the constructor for your Model:

public class TestRequest
{
    public TestRequest()
    {
        this.something = "SomethingDefault";
    }

    public string id { get; set; }

    [DefaultValue("SomethingDefault")]
    public string something { get; set; }
}

As documentation of the DefaultValueAttribute states:

Note

A DefaultValueAttribute will not cause a member to be automatically initialized with the attribute's value. You must set the initial value in your code.

In the case where you're providing no value for your something property, the property is initialized and the ModelBinder doesn't have a value to assign to it and thus the property defaults to its default value.

查看更多
登录 后发表回答