猿问

ASP .Net Core空数组查询参数绑定异常

我有一个如下所示的 WebAPI 控制器:


[Route("api/[controller]")]

[ApiController]

public class ValuesController : ControllerBase

{

    [HttpGet]

    public ActionResult<string> Get([FromQuery]DataFilter dataFilter)

    {

        return string.Join(Environment.NewLine, dataFilter?.Filter?.Select(f => f.ToString()) ?? Enumerable.Empty<string>());

    }

}

没什么特别的,只是一个控制器,它从查询字符串中接收一些数据并将其作为响应输出。它收到的类如下所示:


public class DataFilter

{

    public IEnumerable<FilterType> Filter { get; set; }

}


public enum FilterType

{

    One,

    Two,

    Three,

}

这些只是用于说明问题的示例类,当尝试像这样调用此方法时,这是一个验证错误:


/api/values?filter=

和回应:


{

  "errors": {

    "Filter": [

      "The value '' is invalid."

    ]

  },

  "title": "One or more validation errors occurred.",

  "status": 400,

  "traceId": "80000164-0002-fa00-b63f-84710c7967bb"

}

如果我将我的 FilterType 设置为可为空,它会起作用,但在这种情况下,数组只包含空值。如果这样使用:


/api/values?filter=&filter=&filter=

它将只包含 3 个空值。我希望它只是空的或 null,因为没有传递任何实际值。


ASP .Net Core github 帐户包含一些类似的问题,但据报道它们已在我正在使用的 2.2 中修复。但也许它们是不同的,或者我误解了一些东西。


EDIT_0:只是为了说明我对可空性的意思。


如果我将课程更改为:


public IEnumerable<FilterType?> Filter { get; set; } //notice that nullable is added to an Enum, not the list

然后当这样调用时:


/api/values?filter=&filter=&filter=

我的“过滤器”属性中有 3 个元素。所有空值。不完全是我所期望的。作为解决方法很好,但根本不是解决方案。


梵蒂冈之花
浏览 107回答 3
3回答

哆啦的时光机

您可以创建自定义模型绑定器,其任务是删除默认生成的验证错误CollectionModelBinder。这在您的情况下应该足够了,因为默认模型活页夹按您需要的方式工作,不会向集合中添加无效值。public class EmptyCollectionModelBinder : CollectionModelBinder<FilterType>{&nbsp; &nbsp; public EmptyCollectionModelBinder(IModelBinder elementBinder) : base(elementBinder)&nbsp; &nbsp; {&nbsp; &nbsp; }&nbsp; &nbsp; public override async Task BindModelAsync(ModelBindingContext bindingContext)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; await base.BindModelAsync(bindingContext);&nbsp; &nbsp; &nbsp; &nbsp; //removing validation only for this collection&nbsp; &nbsp; &nbsp; &nbsp; bindingContext.ModelState.ClearValidationState(bindingContext.ModelName);&nbsp; &nbsp; }}创建并注册模型活页夹提供程序public class EmptyCollectionModelBinderProvider : IModelBinderProvider{&nbsp; &nbsp; public IModelBinder GetBinder(ModelBinderProviderContext context)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; if (context == null)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; throw new ArgumentNullException(nameof(context));&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; if (context.Metadata.ModelType == typeof(IEnumerable<FilterType>))&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var elementBinder = context.CreateBinder(context.MetadataProvider.GetMetadataForType(typeof(FilterType)));&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new EmptyCollectionModelBinder(elementBinder);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; return null;&nbsp; &nbsp; }}启动.csservices&nbsp; &nbsp; .AddMvc(options =>&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; options.ModelBinderProviders.Insert(0, new EmptyCollectionModelBinderProvider());&nbsp; &nbsp; })

ITMISS

你有几个选择。您可以创建一个自定义模型活页夹来处理您的过滤器类型或者:您可以使用 Nullables 创建 IEnumerable:public&nbsp;IEnumerable<FilterType?>&nbsp;Filter&nbsp;{&nbsp;get;&nbsp;set;&nbsp;}并过滤掉调用代码中的空值:return&nbsp;string.Join(Environment.NewLine,&nbsp;dataFilter?.Filter?.Where(f&nbsp;=>&nbsp;f&nbsp;!=&nbsp;null) &nbsp;&nbsp;&nbsp;.Select(f&nbsp;=>&nbsp;f.ToString())&nbsp;??&nbsp;Enumerable.Empty<string>());

交互式爱情

我已经使用自定义 TypeConverter 解决了这个问题,并转移到 JSON 格式来传递数组(例如 filter=["one","two"])这是我定义它的方式:public class JsonArrayTypeConverter<T> : TypeConverter{&nbsp; &nbsp; private static readonly TypeConverter _Converter = TypeDescriptor.GetConverter(typeof(T));&nbsp; &nbsp; public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) =>&nbsp; &nbsp; &nbsp; &nbsp; sourceType == typeof(string) || TypeDescriptor.GetConverter(sourceType).CanConvertFrom(context, sourceType);&nbsp; &nbsp; public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; try&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return JsonConvert.DeserializeObject<IEnumerable<T>>((string)value);&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; &nbsp; &nbsp; catch (Exception)&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; var dst = _Converter.ConvertFrom(context, culture, value); //in case this is not an array or something is broken, pass this element to a another converter and still return it as a list&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return new T[] { (T)dst };&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }}和全球注册:TypeDescriptor.AddAttributes(typeof(IEnumerable<FilterType>), new TypeConverterAttribute(typeof(JsonArrayTypeConverter<FilterType>)));现在我的过滤器列表中没有空项目,并且还支持具有多种类型支持(枚举、字符串、整数等)的 JSON 列表。唯一的缺点是这不能像以前那样处理传递的元素(例如 filter=one&filter=two&filter=three)。浏览器地址栏中的查询字符串也不好看。
随时随地看视频慕课网APP
我要回答