CommaDelimitedCollectionModelBinder.cs 3.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Threading.Tasks;
  5. using Microsoft.AspNetCore.Mvc.ModelBinding;
  6. using Microsoft.Extensions.Logging;
  7. namespace Jellyfin.Api.ModelBinders;
  8. /// <summary>
  9. /// Comma delimited collection model binder.
  10. /// Returns an empty array of specified type if there is no query parameter.
  11. /// </summary>
  12. public class CommaDelimitedCollectionModelBinder : IModelBinder
  13. {
  14. private readonly ILogger<CommaDelimitedCollectionModelBinder> _logger;
  15. /// <summary>
  16. /// Initializes a new instance of the <see cref="CommaDelimitedCollectionModelBinder"/> class.
  17. /// </summary>
  18. /// <param name="logger">Instance of the <see cref="ILogger{CommaDelimitedCollectionModelBinder}"/> interface.</param>
  19. public CommaDelimitedCollectionModelBinder(ILogger<CommaDelimitedCollectionModelBinder> logger)
  20. {
  21. _logger = logger;
  22. }
  23. /// <inheritdoc/>
  24. public Task BindModelAsync(ModelBindingContext bindingContext)
  25. {
  26. var valueProviderResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
  27. var elementType = bindingContext.ModelType.GetElementType() ?? bindingContext.ModelType.GenericTypeArguments[0];
  28. var converter = TypeDescriptor.GetConverter(elementType);
  29. if (valueProviderResult.Length > 1)
  30. {
  31. var typedValues = GetParsedResult(valueProviderResult.Values, elementType, converter);
  32. bindingContext.Result = ModelBindingResult.Success(typedValues);
  33. }
  34. else
  35. {
  36. var value = valueProviderResult.FirstValue;
  37. if (value is not null)
  38. {
  39. var splitValues = value.Split(',', StringSplitOptions.RemoveEmptyEntries);
  40. var typedValues = GetParsedResult(splitValues, elementType, converter);
  41. bindingContext.Result = ModelBindingResult.Success(typedValues);
  42. }
  43. else
  44. {
  45. var emptyResult = Array.CreateInstance(elementType, 0);
  46. bindingContext.Result = ModelBindingResult.Success(emptyResult);
  47. }
  48. }
  49. return Task.CompletedTask;
  50. }
  51. private Array GetParsedResult(IReadOnlyList<string> values, Type elementType, TypeConverter converter)
  52. {
  53. var parsedValues = new object?[values.Count];
  54. var convertedCount = 0;
  55. for (var i = 0; i < values.Count; i++)
  56. {
  57. try
  58. {
  59. parsedValues[i] = converter.ConvertFromString(values[i].Trim());
  60. convertedCount++;
  61. }
  62. catch (FormatException e)
  63. {
  64. _logger.LogDebug(e, "Error converting value.");
  65. }
  66. }
  67. var typedValues = Array.CreateInstance(elementType, convertedCount);
  68. var typedValueIndex = 0;
  69. for (var i = 0; i < parsedValues.Length; i++)
  70. {
  71. if (parsedValues[i] is not null)
  72. {
  73. typedValues.SetValue(parsedValues[i], typedValueIndex);
  74. typedValueIndex++;
  75. }
  76. }
  77. return typedValues;
  78. }
  79. }