J'essaie d'envoyer un tableau d'entiers à ma méthode d'action, le code ressemble à ceci:
[HttpGet]
public async Task<IActionResult> ServicesByCategoryIds([FromQuery] int[] ids)
{
var services = await _accountsUow.GetServiceProfilesByCategoryIdsAsync(ids);
return Ok(services);
}
J'appelle la méthode comme ceci: https: // localhost: 44343/api/accounts/servicesbycategoryids? Ids = 1 & ids = 2
mais toujours obtenir un tableau vide lorsque j'appelle cette méthode même si je passe les identifiants dans la chaîne de requête. J'utilise .net core 2.1.
tout ce que j'ai googlé suggère que c'est en fait la façon dont cela se fait. . . y a-t-il quelque chose qui me manque ici?
Je vous remercie!
La liaison a échoué pour le paramètre Array
est un problème connu sous Asp.Net Core 2.1
qui a été enregistré Array ou List dans la chaîne de requête n'est pas analysé # 7712 .
Pour une solution temporaire, vous pouvez définir le FromQuery Name Property
comme ci-dessous:
[HttpGet()]
[Route("ServicesByCategoryIds")]
public async Task<IActionResult> ServicesByCategoryIds([FromQuery(Name = "ids")]int[] ids)
{
return Ok();
}
Je crée une nouvelle classe d'api web, avec une seule action.
[Produces("application/json")]
[Route("api/accounts")]
public class AccountsController : Controller
{
[HttpGet]
[Route("servicesbycategoryids")]
public IActionResult ServicesByCategoryIds([FromQuery] int[] ids)
{
return Ok();
}
}
Utilisez ensuite la même URL que la vôtre:
http: // localhost: 2443/api/accounts/servicesbycategoryids? ids = 1 & ids = 2
Ça fonctionne.
Une légère variation sur la réponse de Plamen.
GenericTypeArguments
vide donc remplacé par 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();
}
}
Ensuite, utilisez avec un IEnumerable
[ModelBinder(BinderType = typeof(CustomArrayModelBinder))] IEnumerable<T> ids
... T[] ids
... List<T> ids
Le paramètre peut être dans le chemin ou la requête avec des crochets facultatifs.
[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]
Vous pouvez implémenter le classeur de modèle personnalisé et les ID pour faire partie de l'URI, pas dans la chaîne de requête.
Votre point de terminaison pourrait ressembler à ceci:/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;
}
}
Ensuite, utilisez-le dans votre action:
[HttpGet("({ids})", Name="GetAuthorCollection")]
public IActionResult GetAuthorCollection(
[ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable<Guid> ids)
{
//enter code here
}
J'ai appris cela à partir d'un cours pluriel: Construire une API RESTful avec ASP.NET Core