当我有一个可以为null的参数时,ModelState.IsValid为false

时间:2015-09-30 08:27:32

标签: asp.net-mvc asp.net-web-api model-binding

我在一个全新的MVC Web API项目中重现了我遇到的问题。

这是默认代码,略有修改。

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

如果你转到http://localhost/api/values/5?something=123,那么这样就可以了,isValid是true

如果您转到http://localhost/api/values/5?something=,则isValid为false

我遇到的问题是,如果为可以为空的项提供null或省略的值,ModelState.IsValid会标记验证错误"A value is required but was not present in the request."

ModelState字典也如下所示:

enter image description here

有两个something条目,一个可以为空,我不确定它是否重要。

任何想法如何解决这个问题,以便在省略可空参数或提供为null时模型是有效的?我在我的web api中使用模型验证,如果每个带有可为空参数的方法都会产生模型错误,它就会破坏它。

4 个答案:

答案 0 :(得分:4)

看来默认绑定模型并不完全理解可空类型。如问题所示,它给出了三个参数错误,而不是预期的两个错误。

您可以使用自定义可为空的模型绑定器解决此问题:

模型活页夹

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;
    }
}

<强>用法

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

    return ModelState;
}

改编自asp.net页面:http://www.asp.net/web-api/overview/formats-and-model-binding/parameter-binding-in-aspnet-web-api以进行进一步阅读,并将其设置为类(控制器)级别而非每个参数。

这处理3个有效场景:

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

这首先将“something”视为null。其他任何事情(例如?something=x)都会出错。

如果您将签名更改为

int? somthing

(即删除= null)然后您必须明确提供参数,即除非您调整路线,否则/test/5将不是有效路线。

答案 1 :(得分:0)

从第二个参数中删除默认的空值。如果模型绑定器不是int。

,则将其设置为null

答案 2 :(得分:0)

您必须为可空类型注册自定义模型绑定器,因为默认绑定器也为可为空的参数调用验证器,后者将这些空值视为无效。

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;
    }
}

注册:

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);

        // ...
    }
}

答案 3 :(得分:0)

我找到了一个可行的解决方法(只需从发送的数据中排除空值 - 而不是将值作为空值发送)。

https://stackoverflow.com/a/66712465/908608