정수 배열을 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,4
ASP.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"}
]
나는 원래 @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는 [Route] 속성을 사용할 때 쉼표로 구분 된 엔드 포인트 만 선택하지 못했습니다.
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로 사용자 정의 유형을 문서화하십시오.
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);
}
요청
http://localhost/api/v1/files/2a9937c7-8201-59b7-bc8d-11a9178895d0/tags/BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63,BBA5CD5D-F07D-47A9-8DEE-D19F5FA65F63
정수를 나열 / 배열하려면 가장 쉬운 방법은 쉼표 (,)로 구분 된 문자열 목록을 허용하고 정수 목록으로 변환하는 것입니다. [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)
{
}
}
}
사용자 정의 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);
}
}
장점은 웹 API 메소드의 매개 변수가 매우 단순하다는 것입니다. [FromUri]를 지정할 필요조차 없습니다.
public IEnumerable<Category> GetCategories(StrList categoryIds) {
// code to retrieve categories from database
}
이 예제는 문자열 목록에 대한 것이지만 categoryIds.Select(int.Parse)
대신 IntList를 작성하거나 작성할 수 있습니다.
메소드 유형을 [HttpPost]로 설정하고 하나의 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);
또는 구분 된 항목의 문자열을 전달하여 수신 측의 배열 또는 목록에 넣을 수 있습니다.
이 문제를 이런 식으로 해결했습니다.
정수 목록을 데이터로 보내기 위해 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;
}
하나의 레코드 또는 많은 레코드에 적합합니다. 채우기는 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 등의 기능은 일반적으로 사용되지만 그 용도로 제한되지 않는다는 점을 명심해야합니다.
따라서 post는 일반적으로 웹 API 인터페이스의 작성 컨텍스트에서 사용되지만 해당 용도로 제한되지는 않습니다. html 연습에서 허용하는 모든 목적으로 사용할 수 있는 일반 html 호출입니다.
또한, 무슨 일이 일어나고 있는지에 대한 세부 사항은 요즘 우리가 너무 많이 듣는 "눈을 피우는"사람들에게 숨겨져 있습니다.
웹 API 2.x 인터페이스의 이름 지정 규칙의 유연성과 일반 웹 호출 사용은 스누 퍼가 실제로 다른 일을하고 있다고 생각하게하는 웹 API에 호출을 보냅니다. 예를 들어 "POST"를 사용하여 실제로 데이터를 검색 할 수 있습니다.
내 솔루션은 문자열의 유효성을 검사하는 속성을 만드는 것이 었습니다. 정규 유효성 검사를 포함하여 숫자 만 확인하는 데 사용할 수있는 정규식 유효성 검사를 포함한 여러 가지 일반적인 기능을 수행 한 다음 필요에 따라 정수로 변환합니다 ...
이것이 당신이 사용하는 방법입니다 :
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;
}
}
참고 URL : https://stackoverflow.com/questions/9981330/pass-an-array-of-integers-to-asp-net-web-api
'development' 카테고리의 다른 글
bash 스크립트에서 첫 번째 인수를 제외한 모든 인수를 처리하십시오. (0) | 2020.02.23 |
---|---|
WPF 이미지 리소스 (0) | 2020.02.23 |
상각 상각 시간 (0) | 2020.02.23 |
파이썬에서 try-except-else를 사용하는 것이 좋은 습관입니까? (0) | 2020.02.23 |
Ruby on Rails 마이그레이션에서 열을 고유하게 만들고 색인화하려면 어떻게해야합니까? (0) | 2020.02.23 |