CommaDelimitedArrayModelBinder.cs 3.3 KB

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