Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using Microsoft.AspNetCore.Mvc;
using ReadingRequestBody.Models;
using ReadingRequestBody.SwaggerUtils;
using ReadingRequestBody.OpenApiUtils;
using ReadingRequestBody.Utils;

namespace ReadingRequestBody.Controllers;
Expand All @@ -15,7 +15,7 @@ public IActionResult Index()
return Ok("Web API is ready.");
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-as-string")]
public async Task<IActionResult> ReadAsString()
{
Expand All @@ -24,7 +24,7 @@ public async Task<IActionResult> ReadAsString()
return Ok($"Request Body As String: {requestBody}");
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-as-string-multiple")]
public async Task<IActionResult> ReadAsStringMultiple()
{
Expand All @@ -34,7 +34,7 @@ public async Task<IActionResult> ReadAsStringMultiple()
return Ok($"First: {requestBody}, Second:{requestBodySecond}");
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-multiple-enable-buffering")]
public async Task<IActionResult> ReadMultipleEnableBuffering()
{
Expand All @@ -55,7 +55,7 @@ public IActionResult ReadFromBody([FromBody] PersonItemDto model)
return Ok(message);
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-from-attribute")]
[ReadRequestBody]
public IActionResult ReadFromAttribute()
Expand All @@ -66,7 +66,7 @@ public IActionResult ReadFromAttribute()
return Ok(message);
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-from-action-filter")]
public IActionResult ReadFromActionFilter()
{
Expand All @@ -76,7 +76,7 @@ public IActionResult ReadFromActionFilter()
return Ok(message);
}

[SwaggerEnableRawText]
[RawTextRequest]
[HttpPost("read-from-middleware")]
public IActionResult ReadFromMiddleware()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,3 @@
namespace ReadingRequestBody.Models
{
public class PersonItemDto
{
public string Name { get; set; }
public int Age { get; set; }
}
}
namespace ReadingRequestBody.Models;

public record PersonItemDto(string Name, int Age);
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ReadingRequestBody.OpenApiUtils;

[AttributeUsage(AttributeTargets.Method)]
public class RawTextRequestAttribute : Attribute
{
public RawTextRequestAttribute()
{
MediaType = "text/plain";
}

public string MediaType { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
using Microsoft.AspNetCore.OpenApi;
using Microsoft.OpenApi;

namespace ReadingRequestBody.OpenApiUtils;

public class RawTextRequestOperationTransformer : IOpenApiOperationTransformer
{
public Task TransformAsync(OpenApiOperation operation,
OpenApiOperationTransformerContext context,
CancellationToken cancellationToken)
{
if (context.Description.ActionDescriptor.EndpointMetadata
.OfType<RawTextRequestAttribute>()
.SingleOrDefault() is RawTextRequestAttribute rawTextRequestAttribute)
{
operation.RequestBody = new OpenApiRequestBody
{
Content = new Dictionary<string, OpenApiMediaType>
{
[rawTextRequestAttribute.MediaType] = new OpenApiMediaType
{
Schema = new OpenApiSchema { Type = JsonSchemaType.String }
}
}
};
}

return Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
@@ -1,28 +1,23 @@
using Microsoft.OpenApi.Models;
using ReadingRequestBody.SwaggerUtils;
using ReadingRequestBody.OpenApiUtils;
using ReadingRequestBody.Utils;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton(typeof(ILogger), typeof(Logger<RequestBodyMiddleware>));

builder.Services.AddControllers(options =>
{
options.Filters.Add<ReadRequestBodyActionFilter>();
});

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
builder.Services.AddOpenApi(options =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Reading Request Body", Version = "v1" });
c.OperationFilter<RawTextRequestOperationFilter>();
options.AddOperationTransformer<RawTextRequestOperationTransformer>();
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.MapOpenApi();
}

app.UseMiddleware<RequestBodyMiddleware>();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.12" />
</ItemGroup>
</Project>
</Project>

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Filters;

namespace ReadingRequestBody.Utils;

public class ReadRequestBodyActionFilter : IAsyncActionFilter
{
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var requestPath = context.HttpContext.Request.Path.Value;
var requestPath = context.HttpContext.Request.Path.Value ?? string.Empty;

if (requestPath.IndexOf("read-from-action-filter") > -1)
if (requestPath.Contains("read-from-action-filter", StringComparison.OrdinalIgnoreCase))
{
var requestBody = await context.HttpContext.Request.Body.ReadAsStringAsync();
context.HttpContext.Request.Headers.Append("ReadRequestBodyActionFilter", requestBody);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,32 @@
namespace ReadingRequestBody.Utils;
namespace ReadingRequestBody.Utils;

public class RequestBodyMiddleware
public class RequestBodyMiddleware(RequestDelegate next, ILogger<RequestBodyMiddleware> logger)
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly int MaxContentLength = 1024;

public RequestBodyMiddleware(RequestDelegate next, ILogger logger)
{
_next = next;
_logger = logger;
}
private const int MaxContentLength = 1024;

public async Task Invoke(HttpContext context)
{
var requestPath = context.Request.Path.Value;
var requestPath = context.Request.Path.Value ?? string.Empty;

if (requestPath.IndexOf("read-from-middleware") > -1)
if (requestPath.Contains("read-from-middleware", StringComparison.OrdinalIgnoreCase))
{
context.Request.EnableBuffering();
var requestBody = await context.Request.Body.ReadAsStringAsync(true);

if (requestBody.Length > MaxContentLength)
if (context.Request.ContentLength > MaxContentLength)
{
context.Response.StatusCode = 413;
context.Response.StatusCode = StatusCodes.Status413PayloadTooLarge;
await context.Response.WriteAsync("Request Body Too Large");

return;
}

_logger.LogInformation("Request Body:{@requestBody}", requestBody);
context.Request.EnableBuffering(bufferThreshold: MaxContentLength, bufferLimit: MaxContentLength);
var requestBody = await context.Request.Body.ReadAsStringAsync(true);

logger.LogInformation("Request Body:{@requestBody}", requestBody);
context.Request.Headers.Append("RequestBodyMiddleware", requestBody);
context.Items.Add("RequestBody", requestBody);
context.Request.Body.Position = 0;
}

await _next(context);
await next(context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ namespace Tests
[TestClass]
public class HomeControllerTests
{
private IFixture _fixture;
private Mock<ILogger> _loggerMock;
private IFixture _fixture = null!;
private Mock<ILogger<RequestBodyMiddleware>> _loggerMock = null!;

[TestInitialize]
public void Setup()
{
_fixture = new Fixture();
_loggerMock = new Mock<ILogger>();
_loggerMock = new Mock<ILogger<RequestBodyMiddleware>>();
}

[TestMethod]
Expand All @@ -34,7 +34,7 @@ public void WhenIndexActionCalled_ThenResponseMustBeReturn()
var result = controller.Index();

Assert.AreEqual(typeof(OkObjectResult), result.GetType());
Assert.AreEqual((result as OkObjectResult).Value, "Web API is ready.");
Assert.AreEqual("Web API is ready.", (result as OkObjectResult)!.Value);
}

[TestMethod]
Expand Down Expand Up @@ -71,11 +71,11 @@ public async Task WhenReadFromAttributeActionCalled_ThenResponseMustBeReturn()
ActionDescriptor = controller.ControllerContext.ActionDescriptor
},
new List<IFilterMetadata>(),
new Dictionary<string, object>(),
new Dictionary<string, object?>(),
controller);

var attribute = new ReadRequestBodyAttribute();
await attribute.OnActionExecutionAsync(context, () => Task.FromResult<ActionExecutedContext>(null));
await attribute.OnActionExecutionAsync(context, () => Task.FromResult<ActionExecutedContext>(null!));

var result = controller.ReadFromAttribute();

Expand Down Expand Up @@ -138,7 +138,7 @@ public async Task WhenReadFromActionFilterActionCalled_ThenResponseMustBeReturn(
new RouteData(),
new ActionDescriptor(),
new ModelStateDictionary());
var actionExecutingContext = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object>(), controller: controller);
var actionExecutingContext = new ActionExecutingContext(actionContext, new List<IFilterMetadata>(), new Dictionary<string, object?>(), controller: controller);

Task<ActionExecutedContext> next()
{
Expand All @@ -156,11 +156,11 @@ Task<ActionExecutedContext> next()

private static void TestRequest(IActionResult result, string responsePrefix, string bodyString)
{
var resultValue = result != null ? (result as OkObjectResult).Value : string.Empty;
var resultValue = result != null ? (result as OkObjectResult)!.Value : string.Empty;

Assert.IsNotNull(result);
Assert.AreEqual(typeof(OkObjectResult), result.GetType());
Assert.AreEqual(resultValue, $"{responsePrefix} {bodyString}");
Assert.AreEqual($"{responsePrefix} {bodyString}", resultValue);
}

private static HomeController GetControllerInstance(string bodyString)
Expand Down
21 changes: 11 additions & 10 deletions aspnetcore-webapi/ReadingRequestBody/Tests/Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoFixture" Version="4.18.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.20.69" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.0" />
<PackageReference Include="AutoFixture" Version="4.18.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.0" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="MSTest.TestAdapter" Version="4.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.4.0" />
<PackageReference Include="coverlet.collector" Version="10.0.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.12" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\ReadingRequestBody\ReadingRequestBody.csproj" />
</ItemGroup>
</Project>
</Project>
Loading