정수 배열을 ASP.NET 웹 API에 전달 하시겠습니까?

정수 배열을 전달 해야하는 ASP.NET 웹 API (버전 4) REST 서비스가 있습니다.

내 행동 방법은 다음과 같습니다.

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

그리고 이것은 내가 시도한 URL입니다.

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



답변

[FromUri]매개 변수 앞에 추가하면됩니다 .

GetCategories([FromUri] int[] categoryIds)

그리고 요청을 보내십시오 :

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


답변

으로 필립 W는 지적, 당신은 (PARAM의 실제 타입에 바인딩 수정)이 같은 사용자 정의 모델 바인더에 의존해야 할 수도 있습니다 :

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배열 을 올바르게 바인딩합니다 .


답변

나는 최근 에이 요구 사항을 직접 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; }
}

나는 그렇게 적용하고 있습니다 ( ‘ids’가 아닌 ‘id’를 사용 했으므로 경로에 지정 된 방식입니다).

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

공개 URL은 다음과 같습니다.

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

특정 요구를 충족시키기 위해 이것을 리팩토링해야 할 수도 있습니다.


답변

를 통해 (삭제 등) 동일하거나 유사한 일을 달성하기 위해 – 경우 누군가가 필요 POST대신 FromUri사용 FromBody및 클라이언트 측 (JS / jQuery를) 형식의 PARAM로$.param({ '': categoryids }, true)

씨#:

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

jQuery :

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

가진 것은 $.param({ '': categoryids }, true)그 후 몸이 좋아 urlencode되고 값을 포함 할 것으로 예상됩니다 .net의 것입니다 =1&=2&=3매개 변수 이름없이, 그리고 괄호없이합니다.


답변

웹 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


답변

이 코드를 사용하여 쉼표로 구분 된 값 / 값 배열을 사용하여 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"}
]


답변

ASP.NET Core 2.0 솔루션 (Swagger Ready)

입력

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로 사용자 정의 유형을 문서화하십시오.