retornar pasar parametro metodos metodo inicializar enteros declarar como arreglo array c# arrays rest asp.net-web-api

pasar - parametro array c#



¿Pasar una matriz de enteros a ASP.NET Web API? (15)

Solución ASP.NET Core 2.0 (Swagger Ready)

Entrada

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

Código

Escriba al proveedor (cómo MVC sabe qué carpeta usar)

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; } }

Escriba la carpeta real (acceda a todo tipo de información sobre la solicitud, acción, modelos, tipos, lo que sea)

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; } }

Registrarlo con MVC

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

Ejemplo de uso con un controlador bien documentado para 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);

EDITAR: Microsoft recomienda usar un TypeConverter para estos niños de operaciones sobre este enfoque. Así que siga los consejos de pósters a continuación y documente su tipo personalizado con SchemaFilter.

Tengo un servicio REST de la API web ASP.NET (versión 4) donde necesito pasar una matriz de enteros.

Aquí está mi método de acción:

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

Y esta es la URL que he probado:

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


Abordé este tema de esta manera.

Utilicé un mensaje posterior a la api para enviar la lista de enteros como datos.

Luego devolví los datos como un ienumerable.

El código de envío es el siguiente:

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; }

El código de recepción es el siguiente:

// 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; }

Funciona bien para un registro o muchos registros. El relleno es un método sobrecargado que usa 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; }

Esto le permite obtener datos de una tabla compuesta (la lista de ID) y luego devolver los registros que realmente le interesan de la tabla de destino.

Podría hacer lo mismo con una vista, pero esto le da un poco más de control y flexibilidad.

Además, los detalles de lo que está buscando en la base de datos no se muestran en la cadena de consulta. Tampoco tienes que convertir desde un archivo csv.

Debe tener en cuenta que al usar cualquier herramienta como la interfaz web api 2.x es que las funciones obtener, poner, publicar, eliminar, encabezar, etc. tienen un uso general, pero no están restringidas a ese uso.

Por lo tanto, mientras que la publicación se utiliza generalmente en un contexto de creación en la interfaz web de la API, no se limita a ese uso. Es una llamada html normal que se puede usar para cualquier propósito permitido por la práctica html.

Además, los detalles de lo que está sucediendo están ocultos a esos "ojos curiosos" que tanto escuchamos sobre estos días.

La flexibilidad de las convenciones de nomenclatura en la interfaz de la api 2.x de la web y el uso de las llamadas web regulares significa que usted envía una llamada a la api de la web que induce a los fisgones a pensar que realmente está haciendo otra cosa. Puede utilizar "POST" para recuperar datos, por ejemplo.


Como señala Filip W , es posible que tenga que recurrir a un cuaderno de modelos personalizado como este (modificado para enlazar con el tipo de parámetro real):

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; } }

Y luego puedes decir:

/Categories?categoryids=1,2,3,4 y ASP.NET Web API enlazarán correctamente su array categoryIds .


En caso de que alguien necesite: para lograr lo mismo o algo similar (como eliminar) a través de POST lugar de FromUri , use FromBody y en el lado del cliente (JS / jQuery) con un formato de $.param({ '''': categoryids }, true)

do#:

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

jQuery:

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

La cosa con $.param({ '''': categoryids }, true) es que .net esperará que el cuerpo de la publicación contenga un valor urlencoded como =1&=2&=3 sin nombre de parámetro, y sin corchetes.


En lugar de usar un ModelBinder personalizado, también puede usar un tipo personalizado con un 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); } }

La ventaja es que hace que los parámetros del método de API web sean muy simples. Ni siquiera necesitas especificar [FromUri].

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

Este ejemplo es para una Lista de cadenas, pero puede hacer categoryIds.Select(int.Parse) o simplemente escribir una IntList en su lugar.


Hace poco encontré este requisito y decidí implementar un ActionFilter para manejar esto.

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; } }

Lo estoy aplicando así (tenga en cuenta que usé ''id'', no ''ids'', ya que así es como se especifica en mi ruta):

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

Y la url pública sería:

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

Es posible que tenga que refactorizar esto para satisfacer sus necesidades específicas.


Haga el tipo de método [HttpPost], cree un modelo que tenga un parámetro int [] y publique con 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);


Manera fácil de enviar parámetros de matriz a la api web

API

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

Jquery: enviar objeto JSON como params de solicitud

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

../api/categories/GetCategories?categoryIds=1&categoryIds=2&categoryIds=3&categoryIds=4 su URL de solicitud como ../api/categories/GetCategories?categoryIds=1&categoryIds=2&categoryIds=3&categoryIds=4


Mi solución fue crear un atributo para validar cadenas, hace un montón de características comunes adicionales, incluida la validación de expresiones regulares que puede usar para verificar solo los números y luego, los convierto a números enteros según sea necesario ...

Así es como usas:

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; } }


O simplemente puede pasar una cadena de elementos delimitados y colocarla en una matriz o lista en el extremo receptor.


Originalmente utilicé la solución que @Mrchief durante años (funciona muy bien). Pero cuando, cuando agregué Swagger a mi proyecto para la documentación de la API, mi punto final NO aparecía.

Me tomó un tiempo, pero esto es lo que se me ocurrió. Funciona con Swagger, y las firmas de su método API se ven más limpias:

Al final puedes hacer:

// 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 .. } }

Crear una nueva clase: 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() }; }

Crear una nueva clase: StringToIntArrayConverter.cs

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

Notas:

  • https://.com/a/47123965/862011 me apuntó en la dirección correcta
  • Swagger solo no pudo seleccionar los puntos finales delimitados por comas cuando usé el atributo [Ruta]

Puede probar este código para que tome valores separados por comas / una matriz de valores para recuperar un JSON de webAPI

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; } }

Salida:

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


Si desea hacer una lista / conjunto de enteros, la forma más sencilla de hacerlo es aceptar la lista de cadenas separadas por comas (,) y convertirla en una lista de enteros. No se olvide de mencionar [FromUri] attriubte.your url se ve así:

...? 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) { } } }


Solo necesita agregar [FromUri] antes del parámetro, se ve así:

GetCategories([FromUri] int[] categoryIds)

Y enviar solicitud:

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


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)); } }

Uso:

[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); }

Solicitar uri

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