整数の配列をASP.NET Web APIに渡しますか?


427

整数の配列を渡す必要があるASP.NET Web API(バージョン4)RESTサービスがあります。

これが私のアクションメソッドです:

public IEnumerable<Category> GetCategories(int[] categoryIds){
// code to retrieve categories from database
}

そして、これは私が試したURLです:

/Categories?categoryids=1,2,3,4

1
「/ Categories?categoryids = 1&categoryids = 2&categoryids = 3」のようなクエリ文字列を使用すると、「リクエストのコンテンツに複数のパラメーターをバインドできません」というエラーが発生しました。これにより、同じエラーが発生していた人々がここに来ることを願っています。
Josh Noe 2014

1
@ジョシュ[FromUri]を使ったの?public IEnumerable <Category> GetCategories([FromUri] int [] categoryids){...}
Anup Kattel

2
@FrankGormanいいえ、そうではありませんでした。それが私の問題でした。
Josh Noe

回答:


619

[FromUri]パラメータの前に追加する必要があるだけで、次のようになります。

GetCategories([FromUri] int[] categoryIds)

そしてリクエストを送信します:

/Categories?categoryids=1&categoryids=2&categoryids=3 

18
配列内の変数の数がわからない場合はどうなりますか?1000のような場合はどうなりますか?リクエストはそのようなものであってはなりません。
Sahar Ch。

7
これにより、「同じキーのアイテムが既に追加されています。」というエラーが発生します。ただし、categoryids [0] = 1&categoryids [1] = 2&などを受け入れます
Doctor Jones

19
これは受け入れられる答えである必要があります-@Hemanshu Bhojak:あなたの選択を取る時間についてではありませんか?
David Rettenbacher 2015年

12
この理由は、ASP.NET Web API Webサイトからのパラメーターバインディングについての次のステートメントによるものです。「パラメーターが「シンプル」タイプの場合、Web APIはURIから値を取得しようとします。シンプルタイプにはNETプリミティブ型(int、bool、doubleなど)、さらにTimeSpan、DateTime、Guid、10進数、文字列、および文字列から変換できる型コンバーターを備えた任意の型。 int []は単純な型ではありません。
Tr1stan

3
これは私にとってはうまくいきます。ワンポイント。サーバーコードでは、配列パラメーターが最初に来て、それが機能するようになり、その後、他のパラメーターが最初に来る必要があります。リクエストにパラメータを入力する場合、順序は重要ではありません。
きっかけ

102

フィリップWが指摘する、あなたは(パラメータの実際の型に特異的に結合するように変更)、このようなカスタムモデルバインダーに頼る必要がある場合があります。

public IEnumerable<Category> GetCategories([ModelBinder(typeof(CommaDelimitedArrayModelBinder))]long[] categoryIds) 
{
    // do your thing
}

public class CommaDelimitedArrayModelBinder : IModelBinder
{
    public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
    {
        var key = bindingContext.ModelName;
        var val = bindingContext.ValueProvider.GetValue(key);
        if (val != null)
        {
            var s = val.AttemptedValue;
            if (s != null)
            {
                var elementType = bindingContext.ModelType.GetElementType();
                var converter = TypeDescriptor.GetConverter(elementType);
                var values = Array.ConvertAll(s.Split(new[] { ","},StringSplitOptions.RemoveEmptyEntries),
                    x => { return converter.ConvertFromString(x != null ? x.Trim() : x); });

                var typedValues = Array.CreateInstance(elementType, values.Length);

                values.CopyTo(typedValues, 0);

                bindingContext.Model = typedValues;
            }
            else
            {
                // change this line to null if you prefer nulls to empty arrays 
                bindingContext.Model = Array.CreateInstance(bindingContext.ModelType.GetElementType(), 0);
            }
            return true;
        }
        return false;
    }
}

そして、あなたは言うことができます:

/Categories?categoryids=1,2,3,4ASP.NET Web APIはcategoryIds配列を正しくバインドします。


10
これはSRPやSoCに違反する可能性がありますが、これも簡単に継承ModelBinderAttributeできるため、typeof()引数を使用する面倒な構文の代わりに直接使用できます。あなたがしなければならないのは、そのように継承することです:CommaDelimitedArrayModelBinder : ModelBinderAttribute, IModelBinderそして、型定義を基本クラスにプッシュするデフォルトコンストラクタを提供します:public CommaDelimitedArrayModelBinder() : base(typeof(CommaDelimitedArrayModelBinder)) { }
sliderhouserules

それ以外の場合、私はこのソリューションが本当に好きで、私のプロジェクトで使用しています。:)
sliderhouserules

AAサイドノートでは、このソリューションのようなジェネリックでは動作しないSystem.Collections.Generic.List<long>ようbindingContext.ModelType.GetElementType()にのみサポートSystem.Arrayタイプ
ViRuSTriNiTy

@ViRuSTriNiTy:この質問と答えは、特に配列について話します。一般的なリストベースのソリューションが必要な場合、それを実装するのはかなり簡単です。どうすればいいかわからない場合は、別の質問をしてください。
Mrchief

2
@codeMonkey:POSTリクエストでは配列を本文に入れるのは理にかなっていますが、GETリクエストはどうですか?これらは通常、本文にコンテンツがありません。
stakx

40

私は最近この要件に自分で遭遇し、これActionFilterを処理するためにを実装することにしました。

public class ArrayInputAttribute : ActionFilterAttribute
{
    private readonly string _parameterName;

    public ArrayInputAttribute(string parameterName)
    {
        _parameterName = parameterName;
        Separator = ',';
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        if (actionContext.ActionArguments.ContainsKey(_parameterName))
        {
            string parameters = string.Empty;
            if (actionContext.ControllerContext.RouteData.Values.ContainsKey(_parameterName))
                parameters = (string) actionContext.ControllerContext.RouteData.Values[_parameterName];
            else if (actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName] != null)
                parameters = actionContext.ControllerContext.Request.RequestUri.ParseQueryString()[_parameterName];

            actionContext.ActionArguments[_parameterName] = parameters.Split(Separator).Select(int.Parse).ToArray();
        }
    }

    public char Separator { get; set; }
}

私はそれを次のように適用しています(ルートで指定されている方法であるため、「id」ではなく「id」を使用したことに注意してください)。

[ArrayInput("id", Separator = ';')]
public IEnumerable<Measure> Get(int[] id)
{
    return id.Select(i => GetData(i));
}

そして、公開URLは次のようになります。

/api/Data/1;2;3;4

特定のニーズを満たすために、これをリファクタリングする必要がある場合があります。


1
タイプintは、ソリューションでハードコーディングされています(int.Parse)。Imho、@ Mrchiefのソリューションの方が優れています
razon

27

誰かが必要とする場合-のPOST代わりにFromUriを使用FromBodyして同じまたは同様のこと(削除など)を実行し、クライアント側(JS / jQuery)のフォーマットパラメータとして$.param({ '': categoryids }, true)

c#:

public IHttpActionResult Remove([FromBody] int[] categoryIds)

jQuery:

$.ajax({
        type: 'POST',
        data: $.param({ '': categoryids }, true),
        url: url,
//...
});

とのこと$.param({ '': categoryids }, true)は、それが.netはポストボディが=1&=2&=3パラメータ名なしで、ブラケットなしのようなurlencoded値を含むことを期待するということです。


2
POSTに頼る必要はありません。@Lavelの回答を参照してください。
アンドレ・Werlang

3
URIで送信できるデータの量には制限があります。また、実際にはデータを変更しているため、これはGETリクエストであってはなりません。
Worthy7

1
そして、ここでGETを正確にどこで見ましたか?:)
Sofija 2016年

3
@Sofija OPによるとcode to retrieve categories from database、メソッドはPOSTではなくGETメソッドである必要があります。
アジマス2017年

22

配列パラメーターをWeb APIに送信する簡単な方法

API

public IEnumerable<Category> GetCategories([FromUri]int[] categoryIds){
 // code to retrieve categories from database
}

jquery:JSONオブジェクトをリクエストパラメータとして送信

$.get('api/categories/GetCategories',{categoryIds:[1,2,3,4]}).done(function(response){
console.log(response);
//success response
});

それはあなたのようなリクエストURLを生成します ../api/categories/GetCategories?categoryIds=1&categoryIds=2&categoryIds=3&categoryIds=4


3
これは受け入れられた答えとどう違うのですか?元の投稿とは何の関係もないjqueryを介したajaxリクエストの実装を除いて。
sksallaj 2018年

13

このコードを試して、カンマ区切りの値/値の配列を取得し、WebAPIからJSONを取得することができます

 public class CategoryController : ApiController
 {
     public List<Category> Get(String categoryIDs)
     {
         List<Category> categoryRepo = new List<Category>();

         String[] idRepo = categoryIDs.Split(',');

         foreach (var id in idRepo)
         {
             categoryRepo.Add(new Category()
             {
                 CategoryID = id,
                 CategoryName = String.Format("Category_{0}", id)
             });
         }
         return categoryRepo;
     }
 }

 public class Category
 {
     public String CategoryID { get; set; }
     public String CategoryName { get; set; }
 } 

出力:

[
{"CategoryID":"4","CategoryName":"Category_4"}, 
{"CategoryID":"5","CategoryName":"Category_5"}, 
{"CategoryID":"3","CategoryName":"Category_3"} 
]

12

ASP.NET Core 2.0ソリューション(Swagger対応)

入力

DELETE /api/items/1,2
DELETE /api/items/1

コード

プロバイダーの記述(MVCが使用するバインダーを認識する方法)

public class CustomBinderProvider : IModelBinderProvider
{
    public IModelBinder GetBinder(ModelBinderProviderContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException(nameof(context));
        }

        if (context.Metadata.ModelType == typeof(int[]) || context.Metadata.ModelType == typeof(List<int>))
        {
            return new BinderTypeModelBinder(typeof(CommaDelimitedArrayParameterBinder));
        }

        return null;
    }
}

実際のバインダーを作成します(リクエスト、アクション、モデル、タイプなど、あらゆる情報にアクセスします)

public class CommaDelimitedArrayParameterBinder : IModelBinder
{

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {

        var value = bindingContext.ActionContext.RouteData.Values[bindingContext.FieldName] as string;

        // Check if the argument value is null or empty
        if (string.IsNullOrEmpty(value))
        {
            return Task.CompletedTask;
        }

        var ints = value?.Split(',').Select(int.Parse).ToArray();

        bindingContext.Result = ModelBindingResult.Success(ints);

        if(bindingContext.ModelType == typeof(List<int>))
        {
            bindingContext.Result = ModelBindingResult.Success(ints.ToList());
        }

        return Task.CompletedTask;
    }
}

MVCに登録する

services.AddMvc(options =>
{
    // add custom binder to beginning of collection
    options.ModelBinderProviders.Insert(0, new CustomBinderProvider());
});

文書化されたSwaggerのコントローラーでの使用例

/// <summary>
/// Deletes a list of items.
/// </summary>
/// <param name="itemIds">The list of unique identifiers for the  items.</param>
/// <returns>The deleted item.</returns>
/// <response code="201">The item was successfully deleted.</response>
/// <response code="400">The item is invalid.</response>
[HttpDelete("{itemIds}", Name = ItemControllerRoute.DeleteItems)]
[ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)]
[ProducesResponseType(typeof(void), StatusCodes.Status404NotFound)]
public async Task Delete(List<int> itemIds)
=> await _itemAppService.RemoveRangeAsync(itemIds);

編集:マイクロソフトで、このアプローチよりも操作のこれらの子供のためにTypeConverterを使用することをお勧めします。したがって、以下のポスターのアドバイスに従い、SchemaFilterを使用してカスタムタイプを文書化してください。


私はあなたが話しているMSの推奨はこの答えで満たされていると思います:stackoverflow.com/a/49563970/4367683
Machado

これを見た?github.com/aspnet/Mvc/pull/7967特別なバインダーを必要とせずに、クエリ文字列でList <whatever>の解析を開始するための修正を追加したかのように見えます。また、あなたがリンクした投稿はASPNET Coreではなく、私の状況に役立つとは思いません。
Victorio Berra 2018年

最善の、ハックではない答え。
エリックフィリップス

7

カスタムModelBinderを使用する代わりに、TypeConverterでカスタムタイプを使用することもできます。

[TypeConverter(typeof(StrListConverter))]
public class StrList : List<string>
{
    public StrList(IEnumerable<string> collection) : base(collection) {}
}

public class StrListConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value == null)
            return null;

        if (value is string s)
        {
            if (string.IsNullOrEmpty(s))
                return null;
            return new StrList(s.Split(','));
        }
        return base.ConvertFrom(context, culture, value);
    }
}

利点は、Web APIメソッドのパラメーターが非常に単純になることです。[FromUri]を指定する必要すらありません。

public IEnumerable<Category> GetCategories(StrList categoryIds) {
  // code to retrieve categories from database
}

この例は文字列のリスト用ですがcategoryIds.Select(int.Parse)、代わりにIntListを作成するか、単に書き込むこともできます。


このソリューションが多くの票を獲得しなかった理由を理解していない。それは素晴らしく、きれいで、カスタムバインダーやものを追加することなくswaggerで動作します。
Thieme

私の意見では最良/最もクリーンな答え。PhillipMに感謝します!
Leigh Bowers、

7

私は当初、@ Mrchiefのソリューションを長年使用していました(それはうまく機能します)。しかし、APIドキュメントのSwaggerをプロジェクトに追加したときに、エンドポイントが表示されませんでした。

少し時間がかかりましたが、これが思いついたものです。Swaggerで動作し、APIメソッドのシグネチャがよりきれいに見えます。

最後に、次のことができます。

    // GET: /api/values/1,2,3,4 

    [Route("api/values/{ids}")]
    public IHttpActionResult GetIds(int[] ids)
    {
        return Ok(ids);
    }

WebApiConfig.cs

public static class WebApiConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Allow WebApi to Use a Custom Parameter Binding
        config.ParameterBindingRules.Add(descriptor => descriptor.ParameterType == typeof(int[]) && descriptor.ActionDescriptor.SupportedHttpMethods.Contains(HttpMethod.Get)
                                                           ? new CommaDelimitedArrayParameterBinder(descriptor)
                                                           : null);

        // Allow ApiExplorer to understand this type (Swagger uses ApiExplorer under the hood)
        TypeDescriptor.AddAttributes(typeof(int[]), new TypeConverterAttribute(typeof(StringToIntArrayConverter)));

        // Any existing Code ..

    }
}

新しいクラスを作成します:CommaDelimitedArrayParameterBinder.cs

public class CommaDelimitedArrayParameterBinder : HttpParameterBinding, IValueProviderParameterBinding
{
    public CommaDelimitedArrayParameterBinder(HttpParameterDescriptor desc)
        : base(desc)
    {
    }

    /// <summary>
    /// Handles Binding (Converts a comma delimited string into an array of integers)
    /// </summary>
    public override Task ExecuteBindingAsync(ModelMetadataProvider metadataProvider,
                                             HttpActionContext actionContext,
                                             CancellationToken cancellationToken)
    {
        var queryString = actionContext.ControllerContext.RouteData.Values[Descriptor.ParameterName] as string;

        var ints = queryString?.Split(',').Select(int.Parse).ToArray();

        SetValue(actionContext, ints);

        return Task.CompletedTask;
    }

    public IEnumerable<ValueProviderFactory> ValueProviderFactories { get; } = new[] { new QueryStringValueProviderFactory() };
}

新しいクラスを作成します:StringToIntArrayConverter.cs

public class StringToIntArrayConverter : TypeConverter
{
    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
    }
}

ノート:

  • https://stackoverflow.com/a/47123965/862011は私を正しい方向に向けました
  • [ルート]属性を使用すると、Swaggerがカンマ区切りのエンドポイントの選択に失敗するだけでした。

1
誰かがこれが使用するライブラリの情報が必要な場合。以下は、「CommaDelimitedArrayParameterBinder」の使用方法です。System.Collections.Generic;を使用します。System.Linqを使用します。System.Threadingを使用します。System.Threading.Tasksを使用します。System.Web.Http.Controllersを使用します。System.Web.Http.Metadataを使用します。System.Web.Http.ModelBindingを使用します。System.Web.Http.ValueProvidersを使用します。System.Web.Http.ValueProviders.Providersを使用します。
SteckDEV

6
public class ArrayInputAttribute : ActionFilterAttribute
{
    private readonly string[] _ParameterNames;
    /// <summary>
    /// 
    /// </summary>
    public string Separator { get; set; }
    /// <summary>
    /// cons
    /// </summary>
    /// <param name="parameterName"></param>
    public ArrayInputAttribute(params string[] parameterName)
    {
        _ParameterNames = parameterName;
        Separator = ",";
    }

    /// <summary>
    /// 
    /// </summary>
    public void ProcessArrayInput(HttpActionContext actionContext, string parameterName)
    {
        if (actionContext.ActionArguments.ContainsKey(parameterName))
        {
            var parameterDescriptor = actionContext.ActionDescriptor.GetParameters().FirstOrDefault(p => p.ParameterName == parameterName);
            if (parameterDescriptor != null && parameterDescriptor.ParameterType.IsArray)
            {
                var type = parameterDescriptor.ParameterType.GetElementType();
                var parameters = String.Empty;
                if (actionContext.ControllerContext.RouteData.Values.ContainsKey(parameterName))
                {
                    parameters = (string)actionContext.ControllerContext.RouteData.Values[parameterName];
                }
                else
                {
                    var queryString = actionContext.ControllerContext.Request.RequestUri.ParseQueryString();
                    if (queryString[parameterName] != null)
                    {
                        parameters = queryString[parameterName];
                    }
                }

                var values = parameters.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries)
                    .Select(TypeDescriptor.GetConverter(type).ConvertFromString).ToArray();
                var typedValues = Array.CreateInstance(type, values.Length);
                values.CopyTo(typedValues, 0);
                actionContext.ActionArguments[parameterName] = typedValues;
            }
        }
    }

    public override void OnActionExecuting(HttpActionContext actionContext)
    {
        _ParameterNames.ForEach(parameterName => ProcessArrayInput(actionContext, parameterName));
    }
}

使用法:

    [HttpDelete]
    [ArrayInput("tagIDs")]
    [Route("api/v1/files/{fileID}/tags/{tagIDs}")]
    public HttpResponseMessage RemoveFileTags(Guid fileID, Guid[] tagIDs)
    {
        _FileRepository.RemoveFileTags(fileID, tagIDs);
        return Request.CreateResponse(HttpStatusCode.OK);
    }

リクエストURI

http://localhost/api/v1/files/2a9937c7-8201-59b7-bc8d-11a9178895d0/tags/BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63,BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63

@Elsa理解できない部分を指摘していただけませんか?コードはそれ自体を説明するのに非常に明確だと思います。これをすべて英語で説明するのは難しいです。
Waninlezu 2014

@Steve Czettyが私の再構築したバージョンです。アイデアをありがとう
Waninlezu

それはで動作する/区切り文字として?次に、次のようにすることができます:dns / root / mystuff / path / to / some / resourceにマップpublic string GetMyStuff(params string[] pathBits)
RoboJ1M

5

リスト/整数の配列を作成する最も簡単な方法は、コンマ(、)で区切られた文字列のリストを受け入れ、それを整数のリストに変換することです。[FromUri] attriubte.yourのURLは次のようになります。

...?ID = 71&accountID = 1,2,3,289,56

public HttpResponseMessage test([FromUri]int ID, [FromUri]string accountID)
{
    List<int> accountIdList = new List<int>();
    string[] arrAccountId = accountId.Split(new char[] { ',' });
    for (var i = 0; i < arrAccountId.Length; i++)
    {
        try
        {
           accountIdList.Add(Int32.Parse(arrAccountId[i]));
        }
        catch (Exception)
        {
        }
    }
}

なぜList<string>単に代わりに使用するのstringですか?それは1,2,3,289,56あなたの例にある1つの文字列のみを持ちます。編集を提案します。
ダニエル・チューリップ

私のために働いた。List<Guid>しかし、コントローラーが自動的にバインドされないことに驚きました。Asp.net Coreの注釈は[FromQuery]であり、必要ないことに注意してください。
kitsu.eb 2016年

2
1行のLinqバージョンの場合:int [] accountIdArray = accountId.Split( '、')。Select(i => int.Parse(i))。ToArray(); 不正なデータを渡す誰かを隠すので、私はキャッチを避けます。
Steve In CO

3

メソッドタイプを[HttpPost]にし、1つのint []パラメータを持つモデルを作成して、jsonで投稿します。

/* Model */
public class CategoryRequestModel 
{
    public int[] Categories { get; set; }
}

/* WebApi */
[HttpPost]
public HttpResponseMessage GetCategories(CategoryRequestModel model)
{
    HttpResponseMessage resp = null;

    try
    {
        var categories = //your code to get categories

        resp = Request.CreateResponse(HttpStatusCode.OK, categories);

    }
    catch(Exception ex)
    {
        resp = Request.CreateErrorResponse(HttpStatusCode.InternalServerError, ex);
    }

    return resp;
}

/* jQuery */
var ajaxSettings = {
    type: 'POST',
    url: '/Categories',
    data: JSON.serialize({Categories: [1,2,3,4]}),
    contentType: 'application/json',
    success: function(data, textStatus, jqXHR)
    {
        //get categories from data
    }
};

$.ajax(ajaxSettings);

あなたは配列をクラスでラップしています-それはうまくいきます(MVC / WebAPIにもかかわらず)。OPは、ラッパークラスなしで配列にバインドすることでした。
Mrchief

1
元の問題は、ラッパークラスなしでそれを行うことについては何も述べていません。複雑なオブジェクトにクエリパラメータを使用したかっただけです。そのパスを下に行き過ぎると、APIが非常に複雑なjsオブジェクトを取得する必要が生じ、クエリパラメータが失敗します。毎回うまくいく方法を学ぶこともできます。
codeMonkey

public IEnumerable<Category> GetCategories(int[] categoryIds){-ええ、あなたは私が思う別の方法で解釈することができました。しかし、多くの場合、ラッパーを作成するためにラッパークラスを作成したくありません。複雑なオブジェクトがある場合は、それで十分です。これらの単純なケースをサポートすることは、そのままでは機能しないため、OPです。
Mrchief

3
これを行うことPOSTは、実際にはRESTパラダイムに反しています。したがって、そのようなAPIはREST APIにはなりません。
アジマス2017年

1
@Azimuthは、一方のパラダイムを私に与え、もう一方の.NETで機能するもの
codeMonkey

3

または、区切られたアイテムの文字列を渡して、それを受信側の配列またはリストに配置することもできます。


2

私はこの方法でこの問題に対処しました。

整数のリストをデータとして送信するために、APIへの投稿メッセージを使用しました。

次に、データをienumerableとして返しました。

送信コードは次のとおりです。

public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids!=null&&ids.Count()>0)
    {
        try
        {
            using (var client = new HttpClient())
            {
                client.BaseAddress = new Uri("http://localhost:49520/");
                client.DefaultRequestHeaders.Accept.Clear();
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                String _endPoint = "api/" + typeof(Contact).Name + "/ListArray";

                HttpResponseMessage response = client.PostAsJsonAsync<IEnumerable<int>>(_endPoint, ids).Result;
                response.EnsureSuccessStatusCode();
                if (response.IsSuccessStatusCode)
                {
                    result = JsonConvert.DeserializeObject<IEnumerable<Contact>>(response.Content.ReadAsStringAsync().Result);
                }

            }

        }
        catch (Exception)
        {

        }
    }
    return result;
}

受信コードは次のとおりです。

// POST api/<controller>
[HttpPost]
[ActionName("ListArray")]
public IEnumerable<Contact> Post([FromBody]IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids != null && ids.Count() > 0)
    {
        return contactRepository.Fill(ids);
    }
    return result;
}

これは、1つのレコードまたは多くのレコードに対して正常に機能します。塗りつぶしは、DapperExtensionsを使用したオーバーロードメソッドです。

public override IEnumerable<Contact> Fill(IEnumerable<int> ids)
{
    IEnumerable<Contact> result = null;
    if (ids != null && ids.Count() > 0)
    {
        using (IDbConnection dbConnection = ConnectionProvider.OpenConnection())
        {
            dbConnection.Open();
            var predicate = Predicates.Field<Contact>(f => f.id, Operator.Eq, ids);
            result = dbConnection.GetList<Contact>(predicate);
            dbConnection.Close();
        }
    }
    return result;
}

これにより、複合テーブル(IDリスト)からデータをフェッチし、ターゲットテーブルから本当に必要なレコードを返すことができます。

ビューでも同じことができますが、これによりもう少し制御と柔軟性が得られます。

また、データベースから検索する内容の詳細はクエリ文字列には表示されません。また、csvファイルから変換する必要はありません。

Web API 2.xインターフェイスのようなツールを使用する場合は、get、put、post、delete、headなどの関数が一般的に使用されますが、その使用に限定されないことに注意してください。

したがって、投稿は通常、Web APIインターフェースの作成コンテキストで使用されますが、その使用に限定されません。これは、HTMLプラクティスで許可されているあらゆる目的に使用できる通常の HTML呼び出しです。

加えて、現在起こっていることの詳細は、最近よく耳にする「詮索好きな目」から隠されています。

Web API 2.xインターフェースでの命名規則の柔軟性と通常のWeb呼び出しの使用は、実際に他のことをしているとスヌーパーに誤解させるような呼び出しをWeb APIに送信することを意味します。たとえば、「POST」を使用して、実際にデータを取得できます。


2

コンマ区切りの値(プリミティブ、10進数、浮動小数点数、文字列のみ)を対応する配列に変換するカスタムモデルバインダーを作成しました。

public class CommaSeparatedToArrayBinder<T> : IModelBinder
    {
        public bool BindModel(HttpActionContext actionContext, ModelBindingContext bindingContext)
        {
            Type type = typeof(T);
            if (type.IsPrimitive || type == typeof(Decimal) || type == typeof(String) || type == typeof(float))
            {
                ValueProviderResult val = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
                if (val == null) return false;

                string key = val.RawValue as string;
                if (key == null) { bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Wrong value type"); return false; }

                string[] values = key.Split(',');
                IEnumerable<T> result = this.ConvertToDesiredList(values).ToArray();
                bindingContext.Model = result;
                return true;
            }

            bindingContext.ModelState.AddModelError(bindingContext.ModelName, "Only primitive, decimal, string and float data types are allowed...");
            return false;
        }

        private IEnumerable<T> ConvertToDesiredArray(string[] values)
        {
            foreach (string value in values)
            {
                var val = (T)Convert.ChangeType(value, typeof(T));
                yield return val;
            }
        }
    }

そしてコントローラーでの使用方法:

 public IHttpActionResult Get([ModelBinder(BinderType = typeof(CommaSeparatedToArrayBinder<int>))] int[] ids)
        {
            return Ok(ids);
        }

おかげで、私はそれを少しの労力でnetcore 3.1に移植しました。受け入れられた答えは何回もparam nameを指定する必要があるという問題を解決せず、netcore 3.1のデフォルトの操作と同じです
Bogdan Mart

0

私の解決策は、文字列を検証するための属性を作成することでした。これは、数値のみをチェックするために使用できる正規表現の検証を含む、追加の一般的な機能を実行し、後で必要に応じて整数に変換します...

これはあなたの使い方です:

public class MustBeListAndContainAttribute : ValidationAttribute
{
    private Regex regex = null;
    public bool RemoveDuplicates { get; }
    public string Separator { get; }
    public int MinimumItems { get; }
    public int MaximumItems { get; }

    public MustBeListAndContainAttribute(string regexEachItem,
        int minimumItems = 1,
        int maximumItems = 0,
        string separator = ",",
        bool removeDuplicates = false) : base()
    {
        this.MinimumItems = minimumItems;
        this.MaximumItems = maximumItems;
        this.Separator = separator;
        this.RemoveDuplicates = removeDuplicates;

        if (!string.IsNullOrEmpty(regexEachItem))
            regex = new Regex(regexEachItem, RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
    }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var listOfdValues = (value as List<string>)?[0];

        if (string.IsNullOrWhiteSpace(listOfdValues))
        {
            if (MinimumItems > 0)
                return new ValidationResult(this.ErrorMessage);
            else
                return null;
        };

        var list = new List<string>();

        list.AddRange(listOfdValues.Split(new[] { Separator }, System.StringSplitOptions.RemoveEmptyEntries));

        if (RemoveDuplicates) list = list.Distinct().ToList();

        var prop = validationContext.ObjectType.GetProperty(validationContext.MemberName);
        prop.SetValue(validationContext.ObjectInstance, list);
        value = list;

        if (regex != null)
            if (list.Any(c => string.IsNullOrWhiteSpace(c) || !regex.IsMatch(c)))
                return new ValidationResult(this.ErrorMessage);

        return null;
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.