I'm trying to cleanly find a way to retrieve a list of IDs in a query parameter. i.e.
/users?ids=abc123,def4141,ggg555
/users?ids=abce123&def4141&ggg555
I want both routes to be mapped to a single endpoint in .NET Core.
If I use a string, /users?ids=abce123&ids=def4141&ids=ggg555
doesn't work properly since we'll only get the first ID, abce123
public ActionResult GetByIds(string ids) {
// do stuff
}
If I use a list of strings, it'll work for the second route /users?ids=abce123&def4141&ggg555
but not the first.
public ActionResult GetByIds(List<string> ids) {
// do stuff
}
CodePudding user response:
You need a model binder
public class ArrayModelBinder : IModelBinder
{
public Task BindModelAsync(ModelBindingContext bindingContext)
{
if (!bindingContext.ModelMetadata.IsEnumerableType)
{
bindingContext.Result = ModelBindingResult.Failed();
return Task.CompletedTask;
}
var providedValue = bindingContext.ValueProvider
.GetValue(bindingContext.ModelName)
.ToString();
if (string.IsNullOrEmpty(providedValue))
{
bindingContext.Result = ModelBindingResult.Success(null);
return Task.CompletedTask;
}
var genericType = bindingContext.ModelType.GetTypeInfo().GenericTypeArguments[0];
var converter = TypeDescriptor.GetConverter(genericType);
var objectArray = providedValue.Split(new[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Select(x => converter.ConvertFromString(x.Trim()))
.ToArray();
var idsArray = Array.CreateInstance(genericType, objectArray.Length);
objectArray.CopyTo(idsArray, 0);
bindingContext.Model = idsArray;
bindingContext.Result = ModelBindingResult.Success(bindingContext.Model);
return Task.CompletedTask;
}
}
And in your controller:
[HttpGet]
public async Task<IActionResult> GetUsersByIds([FromQuery][ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable<string> ids)
{
// for example something like this to get the users from db
var users = await _service.Users.GetByIdsAsync(ids, trackChanges: false);
return Ok(users);
}
and now this will work: /users?ids=abc123,def4141,ggg555
** Credits to codemaze for this