web-dev-qa-db-ja.com

ASPネットコアWeb APIアクションメソッドHttpGetに配列を渡す

コードが次のように見えるアクションメソッドに整数の配列を送信しようとしています。

[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を使用しています。

私がグーグルで調べたすべてのことは、これが実際にこれが行われる方法であることを示唆しています。 。 。私はここに欠けているものがありますか?

ありがとうございました!

6
zoran djipanov

Arrayパラメータのバインドに失敗したことは、Asp.Net Core 2.1での既知の問題であり、記録されています クエリ文字列の配列またはリストは解析されません#7712

一時的な回避策として、次のようにFromQuery Name Propertyを設定できます。

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

アクションが1つだけの新しいWeb APIクラスを作成します。

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

次に、同じURLを使用します:

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

動いています。

3
kennyzx

プラメンの答えのわずかなバリエーション。

  • 配列には空の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]
0
yshehab

クエリ文字列ではなく、URIの一部となるカスタムモデルバインダーとIDを実装できます。

エンドポイントは次のようになります:/ 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を構築する

0
Plamen Yovchev