

Arrays in ASP.NET MVC Core route parameters
source link: https://joonasw.net/view/arrays-in-aspnet-mvc-core-route-parameters
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

You might like to be able to request for multiple users with a URL like /users/1,2,3
(1, 2, and 3 being the user ids).
On the back-end, we'd like to implement the controller action as something like this:
[HttpGet("/users/{ids}")]
public async Task<IActionResult> GetUsersByIds([FromRoute]int[] ids)
{
var model = await _userRepository.GetUsersByIdsAsync(ids);
return View(model);
}
By default this won't work. You could accept a string and split it in the action, but that wouldn't be very clean.
After spelunking a bit in the ASP.NET Core source code, I found it is the value provider that gives the value to the model binder which then sets the action parameters. So I took the existing RouteValueProvider, and modified it so that it splits the value if the target is an array:
/// <summary>
/// Based on: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ModelBinding/RouteValueProvider.cs
/// </summary>
public class ArraySupportingRouteValueProvider : BindingSourceValueProvider
{
private const string ValueSeparator = ",";
private readonly RouteValueDictionary _values;
private readonly ActionDescriptor _actionDescriptor;
private PrefixContainer _prefixContainer;
public ArraySupportingRouteValueProvider(
BindingSource bindingSource,
RouteValueDictionary values,
ActionDescriptor actionDescriptor)
: base(bindingSource)
{
_values = values;
_actionDescriptor = actionDescriptor;
}
protected PrefixContainer PrefixContainer
{
get
{
if (_prefixContainer == null)
{
_prefixContainer = new PrefixContainer(_values.Keys);
}
return _prefixContainer;
}
}
public override bool ContainsPrefix(string prefix)
{
return PrefixContainer.ContainsPrefix(prefix);
}
public override ValueProviderResult GetValue(string key)
{
if (key is null)
{
throw new ArgumentNullException(nameof(key));
}
if (key.Length == 0)
{
return ValueProviderResult.None;
}
if (_values.TryGetValue(key, out var value))
{
var stringValue = value as string ?? Convert.ToString(value, CultureInfo.InvariantCulture) ?? string.Empty;
var targetIsArrayParam = _actionDescriptor
.Parameters
.FirstOrDefault(p => p.Name.Equals(key))
?.ParameterType.IsArray ?? false;
if (targetIsArrayParam)
{
var values = stringValue.Split(ValueSeparator, StringSplitOptions.RemoveEmptyEntries);
return new ValueProviderResult(new StringValues(values), CultureInfo.InvariantCulture);
}
else
{
return new ValueProviderResult(stringValue, CultureInfo.InvariantCulture);
}
}
else
{
return ValueProviderResult.None;
}
}
}
The idea is that if you are binding a string from array, this will work as the standard value provider. Only if you bind to an array, then we do the split and return that as the result.
Then we need a value provider factory for MVC:
/// <summary>
/// Based on: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ModelBinding/RouteValueProviderFactory.cs
/// </summary>
public class ArraySupportingRouteValueProviderFactory : IValueProviderFactory
{
public Task CreateValueProviderAsync(ValueProviderFactoryContext context)
{
if (context is null)
{
throw new ArgumentNullException(nameof(context));
}
var valueProvider = new ArraySupportingRouteValueProvider(
BindingSource.Path,
context.ActionContext.RouteData.Values,
context.ActionContext.ActionDescriptor);
context.ValueProviders.Add(valueProvider);
return Task.CompletedTask;
}
}
And finally we can replace the default value provider in the Startup
class:
services.AddControllersWithViews(mvc =>
{
mvc.ValueProviderFactories.RemoveType<RouteValueProviderFactory>();
mvc.ValueProviderFactories.Add(new ArraySupportingRouteValueProviderFactory());
});
Now if you call the endpoint with /users/1,2,3
, it should work!
You can also accept string or Guid arrays etc. as the model binder knows how to handle them by default.
Recommend
-
58
AdminLTEAspNetMVC Free admin dashboard template based on https://adminlte.io in Asp.net MVC Table of Cont...
-
18
Using Structuremap in legacy ASP.NET MVC applicationsDependency Injection (DI) was also supported by classic ASP.NET MVC but there was no
-
17
阅读目录: 1.开篇介绍 2.AreaRegistration注册路由(传递路由上下文进行模块化注册) 1】开篇介绍 ASP.NET Routing 路由功能非常强大,设计的也很巧妙;如果说ASP.NETMVC是建立在ASP.NET之上还不如准确的说ASP.NET...
-
15
C# with CSharpFritz - S2 Ep 3: ASP.NET Core MVC, Part 2704 views•Jan 5, 2021292ShareSave
-
11
Migrate to ASP.NET Core MVC from ASP.NET Framework MVC 16 minute read .NET has been around for a while and has had many versions. We...
-
31
From MVC to Minimal APIs with ASP.NET Core 6.0 In 2007, .NET web application development had a much needed evolution with the introduction of ASP.NET MVC, providing native support for the
-
11
I’ve designed this post for folks new to ASP.NET Core web development and want to learn the basic steps it takes to add a new endpoint to an existing ASP.NET Core MVC application. Along the way, we’ll explain the purpose of each step until we...
-
7
Posting AJAX Requests to ASP.NET Core MVC Introduction In the past, I’ve had trouble doing something that is apparently simple: invoking a simple action method in a controller using AJAX. Although it is...
-
5
ASP.NET Core Route Tooling Enhancements in .NET 8 James Newton-King
-
10
模拟ASP.NET Core MVC设计与实现 前...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK