|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable enable
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Mvc.ModelBinding;
/// <summary>
/// An <see cref="IValueProvider"/> adapter for data stored in an <see cref="IFormFileCollection"/>.
/// </summary>
/// <remarks>
/// Unlike most <see cref="IValueProvider"/> instances, <see cref="FormFileValueProvider"/> does not provide any values, but
/// specifically responds to <see cref="ContainsPrefix(string)"/> queries. This allows the model binding system to
/// recurse in to deeply nested object graphs with only values for form files.
/// </remarks>
public sealed class FormFileValueProvider : IValueProvider
{
private readonly IFormFileCollection _files;
private PrefixContainer? _prefixContainer;
/// <summary>
/// Creates a value provider for <see cref="IFormFileCollection"/>.
/// </summary>
/// <param name="files">The <see cref="IFormFileCollection"/>.</param>
public FormFileValueProvider(IFormFileCollection files)
{
_files = files ?? throw new ArgumentNullException(nameof(files));
}
private PrefixContainer PrefixContainer
{
get
{
_prefixContainer ??= CreatePrefixContainer(_files);
return _prefixContainer;
}
}
private static PrefixContainer CreatePrefixContainer(IFormFileCollection formFiles)
{
var fileNames = new List<string>();
var count = formFiles.Count;
for (var i = 0; i < count; i++)
{
var file = formFiles[i];
// If there is an <input type="file" ... /> in the form and is left blank.
// This matches the filtering behavior from FormFileModelBinder
if (file.Length == 0 && string.IsNullOrEmpty(file.FileName))
{
continue;
}
fileNames.Add(file.Name);
}
return new PrefixContainer(fileNames);
}
/// <inheritdoc />
public bool ContainsPrefix(string prefix) => PrefixContainer.ContainsPrefix(prefix);
/// <inheritdoc />
public ValueProviderResult GetValue(string key) => ValueProviderResult.None;
}
|