web-dev-qa-db-fra.com

passage d'un tableau à une méthode d'action web api core net asp HttpGet

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!

6
zoran djipanov

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();
    }
6
Tao Zhou

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.

3
kennyzx

Une légère variation sur la réponse de Plamen.

  • Les tableaux semblent avoir un GenericTypeArguments vide donc remplacé par GetElementType ()
  • Classe renommée pour éviter les conflits avec la classe de structure ArrayModelBinder.
  • Ajout d'une vérification sur le type d'élément selon les besoins.
  • Plus d'options pour entourer le tableau avec des crochets.
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]
0
yshehab

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

0
Plamen Yovchev