ModelState.IsValid is false when I have a nullable parameter

时光总嘲笑我的痴心妄想 提交于 2019-12-08 15:29:25

问题


I reproduced the issue I am having in a brand new MVC Web API project.

This is the default code with a slight modification.

public string Get(int? id, int? something = null)
{
    var isValid = ModelState.IsValid;
    return "value";
}

If you go to http://localhost/api/values/5?something=123 then this works fine, and isValid is true.

If you go to http://localhost/api/values/5?something= then isValid is false.

The issue I am having is that if you provide a null or omitted value for an item that is nullable, the ModelState.IsValid flags a validation error saying "A value is required but was not present in the request."

The ModelState dictionary also looks like this:

with two entries for something, one nullable, which I am not sure if it is significant or not.

Any idea how I can fix this so that the model is valid when nullable parameters are omitted or provided as null? I am using model validation within my web api and it breaks it if every method with a nullable parameter generates model errors.


回答1:


It appears that the default binding model doesn't fully understand nullable types. As seen in the question, it gives three parameter errors rather than the expected two.

You can get around this with a custom nullable model binder:

Model Binder

public class NullableIntModelBinder : IModelBinder
{
    public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType != typeof(int?))
        {
            return false;
        }

        ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
        if (val == null)
        {
            return false;
        }

        string rawvalue = val.RawValue as string;

        // Not supplied : /test/5
        if (rawvalue == null)
        {
            bindingContext.Model = null;
            return true;
        }

        // Provided but with no value : /test/5?something=
        if (rawvalue == string.Empty)
        {
            bindingContext.Model = null;
            return true;
        }

        // Provided with a value : /test/5?something=1
        int result;
        if (int.TryParse(rawvalue, out result))
        {
            bindingContext.Model = result;
            return true;
        }

        bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Cannot convert value to int");
        return false;
    }
}

Usage

public ModelStateDictionary Get(
    int? id, 
    [ModelBinder(typeof(NullableIntModelBinder))]int? something = null)
{
    var isValid = ModelState.IsValid;

    return ModelState;
}

Adapted from the asp.net page: http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api for further reading and an alternative method to set it at the class(controller) level rather than per parameter.

This handles the 3 valid scenarios:

/test/5
/test/5?something=
/test/5?something=2

this first give "something" as null. Anything else (eg ?something=x) gives an error.

If you change the signature to

int? somthing

(ie remove = null) then you must explicitly provide the parameter, ie /test/5 will not be a valid route unless you tweak your routes as well.




回答2:


Remove the default null value from the second parameter. The model binder will set it to null if it's something other than int.




回答3:


You'll have to register a custom model-binder for nullable types as the default binder is calling the validator for nullable parameters as well, and the latter considers those empty values as invalid.

The Model Binder:

public class NullableModelBinder<T> : IModelBinder where T : struct
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

        if (val == null)
            return false;

        var rawVal = val.RawValue as string;

        if (rawVal == null)
            return false;

        var converter = TypeDescriptor.GetConverter(typeof(T));

        if (converter.IsValid(rawVal))
        {
            bindingContext.Model = converter.ConvertFromString(rawVal);
            return true;
        }

        bindingContext.ValidationNode.SuppressValidation = true;
        return false;
    }
}

Registration:

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // ...

        var provider = new SimpleModelBinderProvider(typeof(int?), new NullableModelBinder<int>());
        config.Services.Insert(typeof(ModelBinderProvider), 0, provider);

        // ...
    }
}


来源:https://stackoverflow.com/questions/32861365/modelstate-isvalid-is-false-when-i-have-a-nullable-parameter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!