如何使ASP.NET停止将null解释为字符串

Art*_*tem 13 asp.net query-string asp.net-web-api

我有一个Web API方法:

public List<Task> GetTasks([FromUri] TaskFilter filter)
{

}
Run Code Online (Sandbox Code Playgroud)

该方法具有带可空标识符列表的参数:

public class TaskFilter
{
  public IList<int?> Assignees { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我打电话的时候:

GET /tasks?assignees=null
Run Code Online (Sandbox Code Playgroud)

服务器返回错误:

{
  "message":"The request is invalid.",
  "modelState": {
    "assignees": [ "The value 'null' is not valid for Nullable`1." ]
  }
}
Run Code Online (Sandbox Code Playgroud)

它只有在我传递空字符串时才有效:

GET /tasks?assignees=
Run Code Online (Sandbox Code Playgroud)

但是标准查询字符串转换器(来自JQuery, Angular等)不能以这种方式使用空值.

如何让ASP.NET解释'null'null

Upd:查询字符串可以包含多个标识符,例如:

GET /tasks?assignees=1&assignees=2&assignees=null
Run Code Online (Sandbox Code Playgroud)

Upd2: JQuery将数组中的空值转换为空字符串,ASP.NET将它们解释为null.所以问题是从Angular 1.6($HttpParamSerializerProvider)调用WebAPI

Upd3:我知道解决方法,但我没有要求它们.我想要一个特定问题的解决方案:

  • 这是一种GET方法
  • 方法接受来自Uri的列表
  • 列表可以包含null
  • 这应该是List<int?>因为API文档是自动生成的,我不希望将文本数组视为参数类型
  • 默认情况下,ASP.NET期望空字符串为空值(JQuery.param以这种方式工作)
  • 但是一些客户端库(例如Angular)不会将null数组项转换为空字符串

Pra*_*pta 5

您可以为此特定类型创建自定义模型绑定,从DefaultModelBinder继承,以获取示例:

using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
public class TaskFilterBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, System.Web.Mvc.ModelBindingContext bindingContext)
    {
        var request = controllerContext.HttpContext.Request;

        var assignees = request.QueryString["assignees"];

        if (assignees == "null") // check if assignees is null (string) then return NULL
            return null;
        return assignees;
    }

}
Run Code Online (Sandbox Code Playgroud)

最后,我们需要告知控制器我们希望它使用的绑定.我们可以使用属性指定

[模型绑定器(typeof运算(TaskFilterBinder))]

如下:

public List<Task> GetTasks([FromUri(ModelBinder=typeof(TaskFilterBinder))] TaskFilter filter)
{
// Do your stuff.
}
Run Code Online (Sandbox Code Playgroud)

有关更多参考,请查看自定义模型粘合剂上的此链接.希望,这解决了你的问题.谢谢


Art*_*tem 5

最后,我找到了使用自定义值提供程序的解决方案

using System;
using System.Collections.Generic;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.ValueProviders;
using System.Web.Http.ValueProviders.Providers;
using System.Globalization;
using System.Net.Http;
using System.Web.Http.ModelBinding;

public sealed class NullableValueProviderAttribute : ModelBinderAttribute
{
    private readonly string[] _nullableColumns;

    public NullableValueProviderAttribute(params string[] nullableColumns)
    {
        _nullableColumns = nullableColumns;
    }

    public override IEnumerable<ValueProviderFactory> GetValueProviderFactories(HttpConfiguration configuration)
    {
        return new ValueProviderFactory[] { new NullableValueProviderFactory(_nullableColumns) };
    }
}

public class NullableValueProviderFactory : ValueProviderFactory, IUriValueProviderFactory
{
    private readonly string[] _nullableColumns;

    public NullableValueProviderFactory(string[] nullableColumns)
    {
        _nullableColumns = nullableColumns;
    }

    public override IValueProvider GetValueProvider(HttpActionContext actionContext)
    {
        return new NullableQueryStringValueProvider(actionContext, CultureInfo.InvariantCulture, _nullableColumns);
    }
}

public class NullableQueryStringValueProvider : NameValuePairsValueProvider
{
    private static readonly string[] _nullValues = new string[] { "null", "undefined" };

    private static IEnumerable<KeyValuePair<string, string>> GetQueryNameValuePairs(HttpRequestMessage request, string[] nullableColumns)
    {
        foreach (var pair in request.GetQueryNameValuePairs())
        {
            var isNull = Array.IndexOf(nullableColumns, pair.Key) >= 0 && Array.IndexOf(_nullValues, pair.Value) >= 0;
            yield return isNull ? new KeyValuePair<string, string>(pair.Key, "") : pair;
        };
    }

    public NullableQueryStringValueProvider(HttpActionContext actionContext, CultureInfo culture, string[] nullableColumns) :
        base(GetQueryNameValuePairs(actionContext.ControllerContext.Request, nullableColumns), culture)
    { }
}
Run Code Online (Sandbox Code Playgroud)

并在Web API操作中指定它:

public List<Task> GetTasks([NullableValueProvider("assignees")] TaskFilter filter)
{
}
Run Code Online (Sandbox Code Playgroud)