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

Gar*_*ill 16 model-binding asp.net-web-api

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

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

        ...
    }
Run Code Online (Sandbox Code Playgroud)

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

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

http://myserver/api/files?existingFileId=&linkFromExistingFile=true&previousTrackingId=
Run Code Online (Sandbox Code Playgroud)

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

但是,对ModelState.IsValid返回的调用false,当我检查它们时,它正在抱怨两个可以为空的参数.(模型的其他位没有错误).消息是:

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

为什么它认为价值是必需的/不存在的?当然(a)中的值要求可为空的,和(b)的值是(排序的)本-中的一种方式的空杂交排序?

Mar*_*nes 23

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

FileDto fileDto,
bool linkFromExistingFile,
Guid? previousTrackingId = null,
int? existingFileId = null
Run Code Online (Sandbox Code Playgroud)

好点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 };
    }     
}
Run Code Online (Sandbox Code Playgroud)

  • 很好的答案,这帮助了!我仍然想知道为什么在参数级别应用绑定器神奇地使其工作,而在全局设置它不会.我不喜欢在整个地方对参数应用相同的绑定器. (3认同)

Ber*_*ron 8

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

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)
{
   // ...
}
Run Code Online (Sandbox Code Playgroud)

  • 这应该是正确答案。我认为这个问题是 WebAPI 中的一个错误,否则没有多大意义。用 Binder 装饰所有 args 也不是一个可行的选择。 (2认同)