Changed RequestSizeLimitAttribute to create an authorization filter rather than a resource filter.

[Fixes #6777] RequestSizeLimit is ignored
This commit is contained in:
Kiran Challa 2017-09-09 12:27:40 -07:00
parent de38922601
commit 06f6de6c11
10 changed files with 332 additions and 92 deletions

View File

@ -199,10 +199,10 @@ namespace Microsoft.Extensions.DependencyInjection
ServiceDescriptor.Singleton<IFilterProvider, DefaultFilterProvider>());
//
// Resource Filters
// RequestSizeLimit filters
//
services.TryAddTransient<RequestSizeLimitResourceFilter>();
services.TryAddTransient<DisableRequestSizeLimitResourceFilter>();
services.TryAddTransient<RequestSizeLimitFilter>();
services.TryAddTransient<DisableRequestSizeLimitFilter>();
//
// ModelBinding, Validation

View File

@ -14,8 +14,23 @@ namespace Microsoft.AspNetCore.Mvc
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class DisableRequestSizeLimitAttribute : Attribute, IFilterFactory, IOrderedFilter
{
/// <inheritdoc />
public int Order { get; set; }
/// <summary>
/// Gets the order value for determining the order of execution of filters. Filters execute in
/// ascending numeric value of the <see cref="Order"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
/// </para>
/// <para>
/// The default Order for this attribute is 900 because it must run before ValidateAntiForgeryTokenAttribute and
/// after any filter which does authentication or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
/// </para>
/// <para>
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
/// </para>
/// </remarks>
public int Order { get; set; } = 900;
/// <inheritdoc />
public bool IsReusable => true;
@ -23,7 +38,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var filter = serviceProvider.GetRequiredService<DisableRequestSizeLimitResourceFilter>();
var filter = serviceProvider.GetRequiredService<DisableRequestSizeLimitFilter>();
return filter;
}
}

View File

@ -14,34 +14,26 @@ namespace Microsoft.AspNetCore.Mvc.Internal
/// A filter that sets <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to <c>null</c>.
/// </summary>
public class DisableRequestSizeLimitResourceFilter : IResourceFilter, IRequestSizePolicy
public class DisableRequestSizeLimitFilter : IAuthorizationFilter, IRequestSizePolicy
{
private readonly ILogger _logger;
/// <summary>
/// Creates a new instance of <see cref="DisableRequestSizeLimitResourceFilter"/>.
/// Creates a new instance of <see cref="DisableRequestSizeLimitFilter"/>.
/// </summary>
public DisableRequestSizeLimitResourceFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<DisableRequestSizeLimitResourceFilter>();
}
/// <inheritdoc />
public int Order { get; set; }
/// <inheritdoc />
public void OnResourceExecuted(ResourceExecutedContext context)
public DisableRequestSizeLimitFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<DisableRequestSizeLimitFilter>();
}
/// <summary>
/// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to <c>null</c>.
/// </summary>
/// <param name="context">The <see cref="ResourceExecutingContext"/>.</param>
/// <param name="context">The <see cref="AuthorizationFilterContext"/>.</param>
/// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only,
/// the <see cref="DisableRequestSizeLimitAttribute"/> is not applied.</remarks>
public void OnResourceExecuting(ResourceExecutingContext context)
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context == null)
{

View File

@ -14,32 +14,27 @@ namespace Microsoft.AspNetCore.Mvc.Internal
/// A filter that sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/>
/// to the specified <see cref="Bytes"/>.
/// </summary>
public class RequestSizeLimitResourceFilter : IResourceFilter, IRequestSizePolicy
public class RequestSizeLimitFilter : IAuthorizationFilter, IRequestSizePolicy
{
private readonly ILogger _logger;
/// <summary>
/// Creates a new instance of <see cref="RequestSizeLimitResourceFilter"/>.
/// Creates a new instance of <see cref="RequestSizeLimitFilter"/>.
/// </summary>
public RequestSizeLimitResourceFilter(ILoggerFactory loggerFactory)
public RequestSizeLimitFilter(ILoggerFactory loggerFactory)
{
_logger = loggerFactory.CreateLogger<RequestSizeLimitResourceFilter>();
_logger = loggerFactory.CreateLogger<RequestSizeLimitFilter>();
}
public long Bytes { get; set; }
/// <inheritdoc />
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
/// <summary>
/// Sets the <see cref="IHttpMaxRequestBodySizeFeature.MaxRequestBodySize"/> to <see cref="Bytes"/>.
/// </summary>
/// <param name="context">The <see cref="ResourceExecutingContext"/>.</param>
/// <param name="context">The <see cref="AuthorizationFilterContext"/>.</param>
/// <remarks>If <see cref="IHttpMaxRequestBodySizeFeature"/> is not enabled or is read-only,
/// the <see cref="RequestSizeLimitAttribute"/> is not applied.</remarks>
public void OnResourceExecuting(ResourceExecutingContext context)
public void OnAuthorization(AuthorizationFilterContext context)
{
if (context == null)
{

View File

@ -25,8 +25,23 @@ namespace Microsoft.AspNetCore.Mvc
_bytes = bytes;
}
/// <inheritdoc />
public int Order { get; set; }
/// <summary>
/// Gets the order value for determining the order of execution of filters. Filters execute in
/// ascending numeric value of the <see cref="Order"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Filters are executed in an ordering determined by an ascending sort of the <see cref="Order"/> property.
/// </para>
/// <para>
/// The default Order for this attribute is 900 because it must run before ValidateAntiForgeryTokenAttribute and
/// after any filter which does authentication or login in order to allow them to behave as expected (ie Unauthenticated or Redirect instead of 400).
/// </para>
/// <para>
/// Look at <see cref="IOrderedFilter.Order"/> for more detailed info.
/// </para>
/// </remarks>
public int Order { get; set; } = 900;
/// <inheritdoc />
public bool IsReusable => true;
@ -34,7 +49,7 @@ namespace Microsoft.AspNetCore.Mvc
/// <inheritdoc />
public IFilterMetadata CreateInstance(IServiceProvider serviceProvider)
{
var filter = serviceProvider.GetRequiredService<RequestSizeLimitResourceFilter>();
var filter = serviceProvider.GetRequiredService<RequestSizeLimitFilter>();
filter.Bytes = _bytes;
return filter;
}

View File

@ -1,12 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
@ -14,20 +12,20 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.Internal
{
public class DisableRequestSizeLimitResourceFilterTest
public class DisableRequestSizeLimitFilterTest
{
[Fact]
public void SetsMaxRequestBodySizeToNull()
{
// Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize);
@ -37,16 +35,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void SkipsWhenOverridden()
{
// Arrange
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var disableRequestSizeLimitResourceFilterFinal = new DisableRequestSizeLimitFilter(NullLoggerFactory.Instance);
var authorizationFilterContext = CreateauthorizationFilterContext(
new IFilterMetadata[] { disableRequestSizeLimitResourceFilter, disableRequestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilterFinal.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
disableRequestSizeLimitResourceFilterFinal.OnAuthorization(authorizationFilterContext);
// Assert
Assert.Null(httpMaxRequestBodySize.MaxRequestBodySize);
@ -60,11 +59,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
// Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
@ -79,15 +78,15 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true;
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
@ -101,26 +100,23 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitResourceFilter(loggerFactory);
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var disableRequestSizeLimitResourceFilter = new DisableRequestSizeLimitFilter(loggerFactory);
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { disableRequestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
disableRequestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
disableRequestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
Assert.Equal($"The request body size limit has been disabled.", write.State.ToString());
}
private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters)
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
{
return new ResourceExecutingContext(
CreateActionContext(),
filters,
new List<IValueProviderFactory>());
return new AuthorizationFilterContext(CreateActionContext(), filters);
}
private static ActionContext CreateActionContext()

View File

@ -1,12 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.AspNetCore.Mvc.Abstractions;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using Microsoft.AspNetCore.Routing;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Testing;
@ -14,21 +12,21 @@ using Xunit;
namespace Microsoft.AspNetCore.Mvc.Internal
{
public class RequestSizeLimitResourceFilterTest
public class RequestSizeLimitFilterTest
{
[Fact]
public void SetsMaxRequestBodySize()
{
// Arrange
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
Assert.Equal(12345, httpMaxRequestBodySize.MaxRequestBodySize);
@ -38,18 +36,19 @@ namespace Microsoft.AspNetCore.Mvc.Internal
public void SkipsWhenOverridden()
{
// Arrange
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilter.Bytes = 12345;
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitResourceFilter(NullLoggerFactory.Instance);
var requestSizeLimitResourceFilterFinal = new RequestSizeLimitFilter(NullLoggerFactory.Instance);
requestSizeLimitResourceFilterFinal.Bytes = 0;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
var authorizationFilterContext = CreateauthorizationFilterContext(
new IFilterMetadata[] { requestSizeLimitResourceFilter, requestSizeLimitResourceFilterFinal });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilterFinal.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
requestSizeLimitResourceFilterFinal.OnAuthorization(authorizationFilterContext);
// Assert
Assert.Equal(0, httpMaxRequestBodySize.MaxRequestBodySize);
@ -63,12 +62,12 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory);
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
// Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
@ -83,16 +82,16 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory);
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
httpMaxRequestBodySize.IsReadOnly = true;
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
@ -106,27 +105,24 @@ namespace Microsoft.AspNetCore.Mvc.Internal
var sink = new TestSink();
var loggerFactory = new TestLoggerFactory(sink, enabled: true);
var requestSizeLimitResourceFilter = new RequestSizeLimitResourceFilter(loggerFactory);
var requestSizeLimitResourceFilter = new RequestSizeLimitFilter(loggerFactory);
requestSizeLimitResourceFilter.Bytes = 12345;
var resourceExecutingContext = CreateResourceExecutingContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var authorizationFilterContext = CreateauthorizationFilterContext(new IFilterMetadata[] { requestSizeLimitResourceFilter });
var httpMaxRequestBodySize = new TestHttpMaxRequestBodySizeFeature();
resourceExecutingContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
authorizationFilterContext.HttpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(httpMaxRequestBodySize);
// Act
requestSizeLimitResourceFilter.OnResourceExecuting(resourceExecutingContext);
requestSizeLimitResourceFilter.OnAuthorization(authorizationFilterContext);
// Assert
var write = Assert.Single(sink.Writes);
Assert.Equal($"The maximum request body size has been set to 12345.", write.State.ToString());
}
private static ResourceExecutingContext CreateResourceExecutingContext(IFilterMetadata[] filters)
private static AuthorizationFilterContext CreateauthorizationFilterContext(IFilterMetadata[] filters)
{
return new ResourceExecutingContext(
CreateActionContext(),
filters,
new List<IValueProviderFactory>());
return new AuthorizationFilterContext(CreateActionContext(), filters);
}
private static ActionContext CreateActionContext()

View File

@ -0,0 +1,90 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Xunit;
namespace Microsoft.AspNetCore.Mvc.FunctionalTests
{
public class RequestSizeLimitTest : IClassFixture<MvcTestFixture<BasicWebSite.StartupRequestLimitSize>>
{
// Some tests require comparing the actual response body against an expected response baseline
// so they require a reference to the assembly on which the resources are located, in order to
// make the tests less verbose, we get a reference to the assembly with the resources and we
// use it on all the rest of the tests.
private static readonly Assembly _resourcesAssembly = typeof(BasicTests).GetTypeInfo().Assembly;
public RequestSizeLimitTest(MvcTestFixture<BasicWebSite.StartupRequestLimitSize> fixture)
{
Client = fixture.Client;
}
public HttpClient Client { get; }
[Fact]
public async Task RequestSizeLimitCheckHappens_BeforeAntiforgeryTokenValidation()
{
// Arrange
var request = new HttpRequestMessage();
var kvps = new List<KeyValuePair<string, string>>();
kvps.Add(new KeyValuePair<string, string>("SampleString", new string('p', 1024)));
kvps.Add(new KeyValuePair<string, string>("RequestVerificationToken", "invalid-data"));
// Act
var response = await Client.PostAsync(
"RequestSizeLimit/RequestSizeLimitCheckBeforeAntiforgeryValidation",
new FormUrlEncodedContent(kvps));
// Assert
Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);
var result = await response.Content.ReadAsStringAsync();
Assert.Contains(
"InvalidOperationException: Request content size is greater than the limit size",
result);
}
[Fact]
public async Task AntiforgeryTokenValidationHappens_AfterRequestSizeLimitCheck()
{
// Arrange
var request = new HttpRequestMessage();
var kvps = new List<KeyValuePair<string, string>>();
kvps.Add(new KeyValuePair<string, string>("SampleString", "string"));
kvps.Add(new KeyValuePair<string, string>("RequestVerificationToken", "invalid-data"));
// Act
var response = await Client.PostAsync(
"RequestSizeLimit/RequestSizeLimitCheckBeforeAntiforgeryValidation",
new FormUrlEncodedContent(kvps));
// Assert
Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
}
[Fact]
public async Task DisableRequestSizeLimitOnAction_OverridesControllerLevelSettings()
{
// Arrange
var expected = $"{{\"sampleInt\":10,\"sampleString\":\"{new string('p', 1024)}\"}}";
var request = new HttpRequestMessage();
request.Method = HttpMethod.Post;
request.Content = new StringContent(expected, Encoding.UTF8, "text/json");
request.RequestUri = new Uri("http://localhost/RequestSizeLimit/DisableRequestSizeLimit");
// Act
var response = await Client.SendAsync(request);
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var actual = await response.Content.ReadAsStringAsync();
Assert.Equal(expected, actual);
}
}
}

View File

@ -0,0 +1,37 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using BasicWebSite.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
namespace BasicWebSite.Controllers
{
[RequestSizeLimit(500)]
public class RequestSizeLimitController : Controller
{
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult RequestSizeLimitCheckBeforeAntiforgeryValidation(Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Json(product);
}
[HttpPost]
[DisableRequestSizeLimit]
public IActionResult DisableRequestSizeLimit([FromBody] Product product)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
return Json(product);
}
}
}

View File

@ -0,0 +1,104 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.IO;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http.Features;
using Microsoft.Extensions.DependencyInjection;
namespace BasicWebSite
{
public class StartupRequestLimitSize
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
}
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.Use((httpContext, next) =>
{
var testHttpMaxRequestBodySizeFeature = new TestHttpMaxRequestBodySizeFeature();
httpContext.Features.Set<IHttpMaxRequestBodySizeFeature>(
testHttpMaxRequestBodySizeFeature);
httpContext.Request.Body = new RequestBodySizeCheckingStream(
httpContext.Request.Body,
testHttpMaxRequestBodySizeFeature);
return next();
});
app.UseMvcWithDefaultRoute();
}
private class RequestBodySizeCheckingStream : Stream
{
private readonly Stream _innerStream;
private readonly IHttpMaxRequestBodySizeFeature _maxRequestBodySizeFeature;
public RequestBodySizeCheckingStream(
Stream innerStream,
IHttpMaxRequestBodySizeFeature maxRequestBodySizeFeature)
{
_innerStream = innerStream;
_maxRequestBodySizeFeature = maxRequestBodySizeFeature;
}
public override bool CanRead => _innerStream.CanRead;
public override bool CanSeek => _innerStream.CanSeek;
public override bool CanWrite => _innerStream.CanWrite;
public override long Length => _innerStream.Length;
public override long Position
{
get { return _innerStream.Position; }
set { _innerStream.Position = value; }
}
public override void Flush()
{
_innerStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
if (_maxRequestBodySizeFeature.MaxRequestBodySize != null
&& _innerStream.Length > _maxRequestBodySizeFeature.MaxRequestBodySize)
{
throw new InvalidOperationException("Request content size is greater than the limit size");
}
return _innerStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _innerStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_innerStream.SetLength(value);
}
public override void Write(byte[] buffer, int offset, int count)
{
_innerStream.Write(buffer, offset, count);
}
}
private class TestHttpMaxRequestBodySizeFeature : IHttpMaxRequestBodySizeFeature
{
public bool IsReadOnly => false;
public long? MaxRequestBodySize { get; set; }
}
}
}