-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Background and Motivation
To support full parity between the existing anti-forgery support in MVC and the new anti-forgery middleware, we need to support a metadata-based approach for configuring limits to be used when reading from a form. Also, we need to introduce a mapping options type to support configuring the binding experience for forms.
Proposed API
// Assembly: Microsoft.AspNetCore.Http.Abstractions;
namespace Microsoft.AspNetCore.Http.Metadata;
public class FormMappingOptionsMetadata(int maxCollectionSize, int maxRecursionDepth, int maxKeySize)
{
public int MaxCollectionSize { get; } = maxCollectionSize;
public int MaxRecursionDepth { get; } = maxRecursionDepth;
public int MaxKeyBufferSize { get; } = maxKeySize;
}// Assembly: Microsoft.AspNetCore.Http.Abstractions;
namespace Microsoft.AspNetCore.Http.Metadata;
public interface IRequestFormLimitsMetadata
{
bool BufferBody { get; set; }
int MemoryBufferThreshold { get; set; }
long BufferBodyLengthLimit { get; set; }
int ValueCountLimit { get; set; }
int KeyLengthLimit { get; set; }
int ValueLengthLimit { get; set; }
int MultipartBoundaryLengthLimit { get; set; }
int MultipartHeadersCountLimit { get; set; }
int MultipartHeadersLengthLimit { get; set; }
long MultipartBodyLengthLimit { get; set; }
}// Assembly: Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Builder;
public static class RoutingEndpointConventionBuilderExtensions
{
public static TBuilder WithFormMappingOptions<TBuilder>(this TBuilder builder,
int maxCollectionSize = FormDataReader.DefaultValueCountLimit, // 1024
int maxRecursionDepth = 64, // Same as STJ default
int maxKeySize = FormReader.DefaultKeyLengthLimit /* 1024 * 2 */) where TBuilder : IEndpointConventionBuilder { }
// Defaults map to pre-existing values throughout framework
public static TBuilder WithRequestFormLimits<TBuilder>(this TBuilder builder,
bool bufferBody = false,
int memoryBufferThreshold = DefaultMemoryBufferThreshold, // 1024 * 64
long bufferBodyLengthLimit = DefaultBufferBodyLengthLimit, // 1024 * 1024 * 128
int valueCountLimit = FormReader.DefaultValueCountLimit, // 1024
int keyLengthLimit = FormReader.DefaultKeyLengthLimit, // 1024 * 2
int valueLengthLimit = FormReader.DefaultValueLengthLimit, // 1024 * 1024 * 4,
int multipartBoundaryLengthLimit = DefaultMultipartBoundaryLengthLimit, // 128
int multipartHeadersCountLimit = MultipartReader.DefaultHeadersCountLimit, // 16
int multipartHeaderLengthLimit = MultipartReader.DefaultHeadersLengthLimit, // 1024 * 16,
int multipartBodyLengthLimit = DefaultMultipartBodyLengthLimit, /* 1024 * 1024 * 128 */) where TBuilder : IEndpointConventionBuilder { }
}// Assembly: Microsoft.AspNetCore.Mvc.Core;
namespace Microsoft.AspNetCore.Mvc;
- public class RequestFormLimitsAttribute : Attribute, IFilterFactory, IOrderedFilter {}
+ public class RequestFormLimitsAttribute : Attribute, IFilterFactory, IOrderedFilter, IRequestFormLimitsMetadata {}
Usage Examples
var app = WebApplication.Create();
app.MapPost("/todo-1", ([FromForm] Todo todo) => todo)
.WithFormMappingOptions(10, 10, 2);
app.MapPost("/todo-2", ([FromForm] Todo todo) => todo)
.WithFormMappingOptions(maxRecursionDepth: 5);
app.MapPost("/todo-3", ([FromForm] Todo todo) => todo)
.WithFormMappingOptions(maxRecursionDepth: 5)
.WithRequestFormLimits(valueCountLimit: 4);
var todos = app.MapGroup("/todos").WithRequestFormLimits(bufferBody: true);
todos.MapPost("/4", ([FromForm] Todo todo) => todo)
.WithRequestFormLimits(valueLengthLimit: 1024, multipartHeadersCountLimit: 4)
app.Run();Alternative Designs
For .NET 8, we're intentionally moving with the model of having separate form mapping options types for Blazor and minimal APIs, even though they have the same form mapping infrastructure. The goal is to unify these options types in .NET 9 into a new assembly, but for now, the FormMappingOptionsMetadata is defined only as a concrete implementation and not an interface since we don't anticipate evolving it further in .NET 8 before we unify the two types.
Risks
There are risks and costs to action.