We have a custom action filter that does XSD validation on request bodies to ensure that the XML is validated before the controller action is hit. We created an extension method to read the payload body and ensured that the request body can be read multiple times. The xml seems to validate fine when it's not being written to the disk (the documented 30KB limit), however for payloads exceeding the limit, it only works fine the first time, and the succeeding requests to the same endpoint seems to have been corrupted, part of the XML would be cut off, sometimes part of the XML will be in the beginning, then the entire XML for the next request.
i.e. 1st request body
<?xml version="1.0" encoding="UTF-8"?>
<TestXML>
<Node1>Node1</Node1>
<Node2>
<CNode>Hello</CNode>
</Node2
</TestXML>
Succeeding request 1
<?xml version="1.0" encoding="UTF-8"?>
<TestXML>
<Node1>Node1</Node1>
<Node2>
<CNode>Hello</CNode>
Succeeding request 2
<?xml version="1.0" encoding="UTF-8"?>
<TestXML>
<Node1>Node1</Node1>
<Node2>
<CNode>Hello</CNode><?xml version="1.0" encoding="UTF-8"?>
<TestXML>
<Node1>Node1</Node1>
<Node2>
<CNode>Hello</CNode>
</Node2
</TestXML>
Below is our code
Startup.cs
services.AddScoped<XmlValidationActionFilter>();
app.Use(next => context =>
{
context.Request.EnableBuffering();
return next(context);
});
HttpRequest Extension
string requestBody = null;
// IMPORTANT: Ensure the requestBody can be read multiple times.
request.EnableBuffering();
//// IMPORTANT: Leave the body open so the next middleware/filter/action can read it.
using (var reader = new StreamReader(
request.Body,
Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
leaveOpen: true))
{
requestBody = await reader.ReadToEndAsync();
// IMPORTANT: Reset the request body stream position so the next middleware/filter/action can read it
request.Body.Position = 0;
}
return requestBody;
We use the servicefilter decorator to apply the action filter
[ServiceFilter(typeof(XmlValidationActionFilter))]
public async Task<IActionResult> SetRecord([FromHeader(Name = "SerialNumber")] string serialNumber)
{
var payload = await HttpContext.Request.GetRequestBodyAsync();
}
EDIT Other errors that occur are below:
System.InvalidOperationException: Reading is already in progress.
at System.IO.Pipelines.ThrowHelper.ThrowInvalidOperationException_AlreadyReading()
at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
at System.IO.Pipelines.Pipe.ReadAsync(CancellationToken token)
at System.IO.Pipelines.Pipe.DefaultPipeReader.ReadAsync(CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadAsync(Memory`1 memory, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at System.IO.StreamReader.ReadBufferAsync(CancellationToken cancellationToken)
at System.IO.StreamReader.ReadToEndAsyncInternal()
Exception Info: System.InvalidOperationException: Concurrent reads or writes are not supported.
at System.IO.Pipelines.Pipe.GetReadResult(ReadResult& result)
at System.IO.Pipelines.Pipe.GetReadAsyncResult()
at System.IO.Pipelines.Pipe.DefaultPipeReader.GetResult(Int16 token)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContext.ReadAsync(Memory`1 memory, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.Server.IIS.Core.HttpRequestStream.ReadAsyncInternal(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at Microsoft.AspNetCore.WebUtilities.FileBufferingReadStream.ReadAsync(Memory`1 buffer, CancellationToken cancellationToken)
at System.IO.StreamReader.ReadBufferAsync(CancellationToken cancellationToken)
at System.IO.StreamReader.ReadBufferAsync(CancellationToken cancellationToken)
at System.IO.StreamReader.ReadToEndAsyncInternal()
at System.IO.StreamReader.ReadToEndAsyncInternal()
I updated the following and got it to work