File: EndpointRoutingMiddlewareFormOptionsTest.cs
Web Access
Project: src\src\Http\Routing\test\UnitTests\Microsoft.AspNetCore.Routing.Tests.csproj (Microsoft.AspNetCore.Routing.Tests)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Diagnostics;
using System.Text;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Http.Metadata;
using Microsoft.AspNetCore.Routing.Matching;
using Microsoft.AspNetCore.Routing.TestObjects;
using Microsoft.AspNetCore.InternalTesting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Options;
using Moq;
 
namespace Microsoft.AspNetCore.Routing;
 
public class EndpointRoutingMiddlewareFormOptionsTest
{
    [Fact]
    public async Task SupportsSettingFormOptionsFromMetadata()
    {
        var sink = new TestSink(TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
        var loggerFactory = new TestLoggerFactory(sink, enabled: true);
        var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
 
        var httpContext = CreateHttpContext();
        httpContext.Request.ContentType = "application/x-www-form-urlencoded";
        httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("foo=bar"));
 
        var formOptionsMetadata = new FormOptionsMetadata(bufferBody: false, valueCountLimit: 54);
        var middleware = CreateMiddleware(
            logger: logger,
            matcherFactory: new TestMatcherFactory(isHandled: true, setEndpointCallback: c =>
            {
                c.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(formOptionsMetadata), "myapp"));
            }));
 
        // Act
        await middleware.Invoke(httpContext);
        var _ = httpContext.Request.Form; // Trigger the form feature.
 
        var formFeature = httpContext.Features.Get<IFormFeature>();
        var formOptions = Assert.IsType<FormFeature>(formFeature).FormOptions;
        // Configured values are set
        Assert.False(formOptions.BufferBody);
        Assert.Equal(54, formOptions.ValueCountLimit);
        // Unset values are set to default instead of null
        Assert.Equal(FormOptions.DefaultMemoryBufferThreshold, formOptions.MemoryBufferThreshold);
    }
 
    [Fact]
    public async Task SupportsMergingSettingsFromMultipleFormOptionsMetadata()
    {
        var sink = new TestSink(TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
        var loggerFactory = new TestLoggerFactory(sink, enabled: true);
        var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
 
        var httpContext = CreateHttpContext();
        httpContext.Request.ContentType = "application/x-www-form-urlencoded";
        httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("foo=bar"));
 
        var formOptionsMetadata1 = new FormOptionsMetadata(bufferBody: false, valueCountLimit: 54);
        var formOptionsMetadata2 = new FormOptionsMetadata(valueLengthLimit: 45);
        var formOptionsMetadata3 = new FormOptionsMetadata(bufferBody: true);
        var middleware = CreateMiddleware(
            logger: logger,
            matcherFactory: new TestMatcherFactory(isHandled: true, setEndpointCallback: c =>
            {
                c.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(formOptionsMetadata1, formOptionsMetadata2, formOptionsMetadata3), "myapp"));
            }));
 
        // Act
        await middleware.Invoke(httpContext);
        var _ = httpContext.Request.Form; // Trigger the form feature.
 
        var formFeature = httpContext.Features.Get<IFormFeature>();
        var formOptions = Assert.IsType<FormFeature>(formFeature).FormOptions;
        // Favor the most specific (last) value set for a property
        Assert.True(formOptions.BufferBody);
        Assert.Equal(54, formOptions.ValueCountLimit);
        Assert.Equal(45, formOptions.ValueLengthLimit);
        // Unset values are set to default instead of null
        Assert.Equal(FormOptions.DefaultMemoryBufferThreshold, formOptions.MemoryBufferThreshold);
    }
 
    [Fact]
    public async Task SupportsMergingSettingsFromMetadataAndServices()
    {
        var sink = new TestSink(TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
        var loggerFactory = new TestLoggerFactory(sink, enabled: true);
        var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
        var serviceProvider = new ServiceCollection().Configure<FormOptions>(options =>
        {
            options.BufferBody = true;
            options.ValueLengthLimit = 45;
        }).BuildServiceProvider();
        var httpContextFactory = new DefaultHttpContextFactory(serviceProvider);
        var httpContext = httpContextFactory.Create( new DefaultHttpContext().Features);
        httpContext.Request.ContentType = "application/x-www-form-urlencoded";
        httpContext.Request.Body = new MemoryStream("foo=bar"u8.ToArray());
 
        var formOptionsMetadata = new FormOptionsMetadata(bufferBody: false, valueCountLimit: 54);
        var middleware = CreateMiddleware(
            logger: logger,
            matcherFactory: new TestMatcherFactory(isHandled: true, setEndpointCallback: c =>
            {
                c.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(formOptionsMetadata), "myapp"));
            }));
 
        // Act
        await middleware.Invoke(httpContext);
        var _ = httpContext.Request.Form; // Trigger the form feature.
 
        var formFeature = httpContext.Features.Get<IFormFeature>();
        var formOptions = Assert.IsType<FormFeature>(formFeature).FormOptions;
        // Favor the most specific (last) value set for a property
        Assert.False(formOptions.BufferBody);
        Assert.Equal(54, formOptions.ValueCountLimit);
        Assert.Equal(45, formOptions.ValueLengthLimit);
        // Unset values are set to default instead of null
        Assert.Equal(FormOptions.DefaultMemoryBufferThreshold, formOptions.MemoryBufferThreshold);
    }
 
    [Fact]
    public async Task SettingEndpointManuallyStillResolvesFormOptions()
    {
        var sink = new TestSink(TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
        var loggerFactory = new TestLoggerFactory(sink, enabled: true);
        var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
        var httpContext = CreateHttpContext();
        httpContext.Request.ContentType = "application/x-www-form-urlencoded";
        httpContext.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes("foo=bar"));
 
        var endpointMetadata = new FormOptionsMetadata(bufferBody: true, valueCountLimit: 70);
        var endpoint = new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(endpointMetadata), "myapp");
 
        var formOptionsMetadata = new FormOptionsMetadata(bufferBody: false, valueCountLimit: 54);
        var middleware = CreateMiddleware(
            logger: logger,
            matcherFactory: new TestMatcherFactory(isHandled: true, setEndpointCallback: c =>
            {
                c.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(formOptionsMetadata), "myapp"));
            }));
 
        // Act
        await middleware.Invoke(httpContext);
        httpContext.SetEndpoint(endpoint);
        var _ = httpContext.Request.Form; // Trigger the form feature.
 
        var formFeature = httpContext.Features.Get<IFormFeature>();
        var formOptions = Assert.IsType<FormFeature>(formFeature).FormOptions;
        // Favor the most specific (last) value set for a property
        Assert.True(formOptions.BufferBody);
        Assert.Equal(70, formOptions.ValueCountLimit);
        // Unset values are set to default instead of null
        Assert.Equal(FormOptions.DefaultMemoryBufferThreshold, formOptions.MemoryBufferThreshold);
    }
 
    [Fact]
    public async Task OptionsNotSetForNonFormRequests()
    {
        var sink = new TestSink(TestSink.EnableWithTypeName<EndpointRoutingMiddleware>);
        var loggerFactory = new TestLoggerFactory(sink, enabled: true);
        var logger = new Logger<EndpointRoutingMiddleware>(loggerFactory);
        var httpContext = new DefaultHttpContext();
        var endpointMetadata = new FormOptionsMetadata(bufferBody: true, valueCountLimit: 70);
        var endpoint = new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(endpointMetadata), "myapp");
 
        var formOptionsMetadata = new FormOptionsMetadata(bufferBody: false, valueCountLimit: 54);
        var middleware = CreateMiddleware(
            logger: logger,
            matcherFactory: new TestMatcherFactory(isHandled: true, setEndpointCallback: c =>
            {
                c.SetEndpoint(new Endpoint(c => Task.CompletedTask, new EndpointMetadataCollection(formOptionsMetadata), "myapp"));
            }));
 
        // Act
        await middleware.Invoke(httpContext);
        httpContext.SetEndpoint(endpoint);
 
        var formFeature = httpContext.Features.Get<IFormFeature>();
        Assert.Null(formFeature);
    }
 
    private HttpContext CreateHttpContext()
    {
        var httpContext = new DefaultHttpContext
        {
            RequestServices = new TestServiceProvider(),
            Request =
            {
                ContentType = "multipart/form-data; boundary=----WebKitFormBoundarymx2fSWqWSd0OxQqq",
                Method = "POST",
            }
        };
 
        return httpContext;
    }
 
    private EndpointRoutingMiddleware CreateMiddleware(
        ILogger<EndpointRoutingMiddleware> logger = null,
        MatcherFactory matcherFactory = null,
        DiagnosticListener listener = null,
        RequestDelegate next = null)
    {
        next ??= c => Task.CompletedTask;
        logger ??= new Logger<EndpointRoutingMiddleware>(NullLoggerFactory.Instance);
        matcherFactory ??= new TestMatcherFactory(true);
        listener ??= new DiagnosticListener("Microsoft.AspNetCore");
        var metrics = new RoutingMetrics(new TestMeterFactory());
 
        var middleware = new EndpointRoutingMiddleware(
            matcherFactory,
            logger,
            new DefaultEndpointRouteBuilder(Mock.Of<IApplicationBuilder>()),
            new DefaultEndpointDataSource(),
            listener,
            Options.Create(new RouteOptions()),
            metrics,
            next);
 
        return middleware;
    }
}