为什么我的ApiController方法具有可为空参数的ModelState.IsValid失败?

时间:2012-08-17 13:11:49

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

我有一个接受多个参数的ApiController方法,如下所示:

    // POST api/files
    public HttpResponseMessage UploadFile
    (
        FileDto fileDto,
        int? existingFileId,
        bool linkFromExistingFile,
        Guid? previousTrackingId
    )
    {
        if (!ModelState.IsValid)
            return Request.CreateResponse(HttpStatusCode.BadRequest);

        ...
    }

当我对此发布POST时,我将FileDto对象放在请求的正文中,并将其他参数放在查询字符串上。

我已经发现我不能简单地省略可以为空的参数 - 我需要将它们放在具有空值的查询字符串上。因此,当我不想为可为空的参数指定值时,我的查询看起来像这样:

http://myserver/api/files?existingFileId=&linkFromExistingFile=true&previousTrackingId=

这与我的控制器方法匹配,并且当执行该方法时,可空参数确实是null(正如您所期望的那样)。

然而,对ModelState.IsValid的调用会返回false,当我检查这些错误时,它会抱怨这两个可空参数。 (模型的其他位没有错误)。消息是:

  

值是必需的,但请求中不存在。

为什么它认为某个值是必需的/不存在的?当然(a)一个值是可以为空,而(b)一个值是(某种类型的)存在 - 以某种方式为空?

2 个答案:

答案 0 :(得分:21)

除了第一个答案之外,您应该能够让代码正常工作,如果将所有可选项移动到方法声明的末尾,则允许省略url上的前缀,并且我总是将它们设置为NULL以获得良好的度量:

FileDto fileDto,
bool linkFromExistingFile,
Guid? previousTrackingId = null,
int? existingFileId = null

<强>但是

好点re:带有前缀的空URL值...是否与NULL相同...考虑字符串,?q=是空字符串还是空?

我试图找到框架中的确切逻辑(并继续查看)引发这些错误,但在我的实验中,我确实发现直接在URL参数上指定绑定器似乎绕过逻辑并允许空值在没有模型绑定错误的前缀之后。

像这样:

public class ValuesController : ApiController
{
    // GET api/values
    public IEnumerable<string> Get(
        [FromUri(BinderType = typeof(TypeConverterModelBinder))] string q = null,
        [FromUri(BinderType = typeof(TypeConverterModelBinder))] int? value = null)
    {
        if (!ModelState.IsValid)
        {
            throw new HttpResponseException(HttpStatusCode.BadRequest);
        }

        return new string[] { value.HasValue ? value.Value.ToString() : "", q };
    }     
}

答案 1 :(得分:7)

我通过将所有参数移动到单个类来解决这个问题。

public class UploadFileModel {
   public FileDto FileDto { get; set; }
   public int? ExistingFileId { get; set; }
   public bool LinkFromExistingFile { get; set; }
   public Guid? PreviousTrackingId { get; set; }
}

public HttpResponseMessage UploadFile([FromUri]UploadFileModel model)
{
   // ...
}