将数组传递给 asp net core web api 操作方法 HttpGet
Posted
技术标签:
【中文标题】将数组传递给 asp net core web api 操作方法 HttpGet【英文标题】:passing an array to a asp net core web api action method HttpGet 【发布时间】:2018-12-20 09:39:59 【问题描述】:我正在尝试向我的操作方法发送一个整数数组,代码如下所示:
[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 核心 2.1。
我在谷歌上搜索的所有内容都表明这实际上就是这样做的方式。 . . 我在这里有什么遗漏吗?
谢谢!
【问题讨论】:
这能回答你的问题吗? Pass an array of integers to ASP.NET Web API? 【参考方案1】:答案是简单地用[FromQuery]
属性修饰数组就可以使绑定工作。如果没有该属性,它将无法绑定。就是这样,上面@kennyzx 的答案是最好的,但我觉得需要像这样简单地说明这一点:[FromQuery]
就是你所需要的。我不知道为什么这些其他答案会选择ModelBinder
路线,这可能是某些场景所需要的,但就我而言,我确信与其他许多人一样,关键是不要忘记应用[FromQuery]
属性.
public ActionResult GetFoo(int id, [FromQuery] Guid[] someIds) ...
【讨论】:
【参考方案2】:与 Plamen 的答案略有不同。
数组似乎有一个空的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() ??
bindingContext.ModelType.GetTypeInfo().GenericTypeArguments.FirstOrDefault();
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<T>
、IList<T>
或数组T[]
[ModelBinder(BinderType = typeof(CustomArrayModelBinder))] IEnumerable<T> ids
... T[] ids
... IList<T> ids
参数可以在路径或带有可选括号的查询中。
[Route("resources/ids")]
resource/ids/1,2,3
resource/ids/(1,2,3)
resource/ids/[1,2,3]
[Route("resources")]
resource?ids=1,2,3
resource?ids=(1,2,3)
resource?ids=[1,2,3]
【讨论】:
var elementType = bindingContext.ModelType.GetElementType();在您的 CustomModelArrayBinder 中将仅适用于 Array 类型。所以 IEnumerable您可以将自定义模型绑定器和 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<int> ids)
//enter code here
从一门复数课程中学到这一点:使用 ASP.NET Core 构建 RESTful API
【讨论】:
我强烈推荐kevin dockx课程!【参考方案4】:您可以使用 [FromBody]
代替 [ab] 使用查询字符串(考虑 1000 个 ID),并将 ID 列表作为 JSON 数组传递:
public IActionResult ServicesByCategoryIds([FromBody] int[] ids)
只要涉及到 OpenAPI/Swagger,就会生成适当的规范:
"parameters": [
"name": "ids",
"in": "body",
"required": true,
"schema":
"type": "array",
"items":
"type": "integer",
"format": "int32"
],
【讨论】:
如您所见,您需要使用正确的 Content-Type 将 HTTP 动词更改为 HttpPost,因为现在您正在通过 Body 传递参数。该问题与 HttpGet 动词有关【参考方案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();
【讨论】:
当我按照您链接的讨论中的建议删除 ApiControllerAttribute 时,似乎也可以工作。 帮助很大!!谢谢 推荐使用下面来自 yshehab 或 Plamen Yovchev 的更新答案【参考方案6】:我创建了一个新的 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
它正在工作。
【讨论】:
以上是关于将数组传递给 asp net core web api 操作方法 HttpGet的主要内容,如果未能解决你的问题,请参考以下文章
通过 JSON 对象将 Session 对象中的字节数组传递给 Web 服务(Asp.Net 2.0 asmx)
如何在 ASP.NET Web API 实现中将数组传递给 OData 函数?