将数组传递给ASP网络核心Web API操作方法HttpGet

时间:2018-07-12 08:35:04

标签: asp.net-core .net-core asp.net-core-webapi

我正在尝试将整数数组发送给我的操作方法,代码看起来像这样:

[HttpGet]
    public async Task<IActionResult> ServicesByCategoryIds([FromQuery] int[] ids)
    {
        var services = await _accountsUow.GetServiceProfilesByCategoryIdsAsync(ids);
        return Ok(services);
    }

我这样调用该方法:https://localhost:44343/api/accounts/servicesbycategoryids?ids=1&ids=2

但是当我调用此方法时,即使在查询字符串中传递ID时,始终会得到一个空数组。我正在使用.net core 2.1。

我搜索过的所有内容都表明这实际上是完成操作的方式。 。 。 这里有我想念的东西吗?

谢谢!

5 个答案:

答案 0 :(得分:5)

Array参数的绑定失败是Asp.Net Core 2.1下的一个已知问题,已记录Array or List in query string does not get parsed #7712

要暂时解决此问题,您可以像下面这样设置FromQuery Name Property

        [HttpGet()]
    [Route("ServicesByCategoryIds")]
    public async Task<IActionResult> ServicesByCategoryIds([FromQuery(Name = "ids")]int[] ids)
    {            
        return Ok();
    }

答案 1 :(得分:1)

我创建一个新的Web api类,只需执行一次操作即可。

[Produces("application/json")]
[Route("api/accounts")]
public class AccountsController : Controller
{
    [HttpGet]
    [Route("servicesbycategoryids")]
    public IActionResult ServicesByCategoryIds([FromQuery] int[] ids)
    {
        return Ok();
    }
}

然后使用与您相同的网址:

http://localhost:2443/api/accounts/servicesbycategoryids?ids=1&ids=2

正在工作。

答案 2 :(得分:0)

您可以将自定义模型绑定程序和ID实施为URI的一部分,而不是查询字符串中的一部分。

您的端点可能看起来像这样: / api / accounts / servicesbycategoryids /(1,2)

public class ArrayModelBinder : IModelBinder
    {
        public Task BindModelAsync(ModelBindingContext bindingContext)
        {
            // Our binder works only on enumerable types
            if (!bindingContext.ModelMetadata.IsEnumerableType)
            {
                bindingContext.Result = ModelBindingResult.Failed();
                return Task.CompletedTask;
            }

            // Get the inputted value through the value provider
            var value = bindingContext.ValueProvider
                .GetValue(bindingContext.ModelName).ToString();

            // If that value is null or whitespace, we return null
            if (string.IsNullOrWhiteSpace(value))
            {
                bindingContext.Result = ModelBindingResult.Success(null);
                return Task.CompletedTask;
            }

            // The value isn't null or whitespace, 
            // and the type of the model is enumerable. 
            // Get the enumerable's type, and a converter 
            var elementType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
            var converter = TypeDescriptor.GetConverter(elementType);

            // Convert each item in the value list to the enumerable type
            var values = value.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
                .Select(x => converter.ConvertFromString(x.Trim()))
                .ToArray();

            // Create an array of that type, and set it as the Model value 
            var typedValues = Array.CreateInstance(elementType, values.Length);
            values.CopyTo(typedValues, 0);
            bindingContext.Model = typedValues;

            // return a successful result, passing in the Model 
            bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
            return Task.CompletedTask;
        }
    }

然后在您的操作中使用它:

[HttpGet("({ids})", Name="GetAuthorCollection")]
        public IActionResult GetAuthorCollection(
            [ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable<Guid> ids)
        {          
            //enter code here
        }

从多元视角课程中学到了这一点:使用ASP.NET Core构建RESTful API

答案 3 :(得分:0)

普拉门的答案略有不同。

  • 数组似乎有一个空的GenericTypeArguments,因此将其替换为GetElementType()
  • 重命名该类以避免与框架类ArrayModelBinder冲突。
  • 根据需要添加了对元素类型的检查。
  • 使用括号将数组括起来的更多选项。
public class CustomArrayModelBinder : IModelBinder
{
    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (!bindingContext.ModelMetadata.IsEnumerableType)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return Task.CompletedTask;
        }

        var value = bindingContext.ValueProvider
            .GetValue(bindingContext.ModelName)
            .ToString();

        if (string.IsNullOrWhiteSpace(value))
        {
            bindingContext.Result = ModelBindingResult.Success(null);
            return Task.CompletedTask;
        }

        var elementType = bindingContext.ModelType.GetElementType();

        if (elementType == null)
        {
            bindingContext.Result = ModelBindingResult.Failed();
            return Task.CompletedTask;
        }

        var converter = TypeDescriptor.GetConverter(elementType);

        var values = value.Split(',', StringSplitOptions.RemoveEmptyEntries)
            .Select(x => converter.ConvertFromString(Clean(x)))
            .ToArray();

        var typedValues = Array.CreateInstance(elementType, values.Length);
        values.CopyTo(typedValues, 0);
        bindingContext.Model = typedValues;

        bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
        return Task.CompletedTask;
    }

    private static string Clean(string str)
    {
        return str.Trim('(', ')').Trim('[', ']').Trim();
    }
}

然后与IEnumerable

一起使用
[ModelBinder(BinderType = typeof(CustomArrayModelBinder))] IEnumerable<T> ids
                                                       ... T[] ids
                                                       ... List<T> ids

该参数可以在路径中或带有可选括号的查询中。

[Route("resource/{ids}")]

resource/ids/1,2,3
resource/ids/(1,2,3)
resource/ids/[1,2,3]

[Route("resource")]

resource?ids=1,2,3
resource?ids=(1,2,3)
resource?ids=[1,2,3]

答案 4 :(得分:-1)

您可以使用SELECT id, l1.campaign, event_type, COALESCE(date_offered, date_ordered, date_delivered) AS event_date, COALESCE(l1.quantity,l2.quantity) AS quantity FROM logistics l1 JOIN ( SELECT campaign, MAX(quantity) AS quantity FROM logistics WHERE event_type = 'ordered' GROUP BY campaign ) l2 ON l2.campaign = l1.campaign 而不是[ab]使用查询字符串(考虑1000个ID),并将ID列表作为JSON数组传递:

[FromBody]

只要涉及OpenAPI / Swagger,就会生成正确的规范:

public IActionResult ServicesByCategoryIds([FromBody] int[] ids)

enter image description here