I have a third-party proprietary application, for which I need to write an API endpoint in my ASP.NET Core 5.0 web API application.
The third party application sends out an HTTP post request, with only binary data in the request body, alongside the content type application/x-www-form-urlencoded or, sometimes, application/octet-stream (kind of random, but the data is the same).
My action handler looks like this:
[Route("~/Validation")]
[ApiController]
public class ValidationController : ControllerBase
{
[HttpPost("{requestId}")]
[Consumes(@"application/octet-stream", @"application/x-www-form-urlencoded")]
[Produces(@"application/octet-stream")]
public async Task<IActionResult> Validation_Post([FromRoute] string requestId)
{
byte[] rawRequestBody = Array.Empty<byte>();
{
long streamInitialPos = 0;
if (Request.Body.CanSeek) // rewind for this read.
{
streamInitialPos = Request.Body.Position;
Request.Body.Seek(0, SeekOrigin.Begin);
}
using (var ms = new MemoryStream())
{
await Request.Body.CopyToAsync(ms);
rawRequestBody = ms.ToArray() ?? throw new NullReferenceException();
}
if (Request.Body.CanSeek) // rewind to initial position.
Request.Body.Seek(streamInitialPos, SeekOrigin.Begin);
}
// TODO: Handle rawRequestBody data.
return new FileContentResult(new byte[] { 1 }, @"application/octet-stream")
{
EnableRangeProcessing = true,
LastModified = DateTime.UtcNow
};
}
When the third-party application is sending its HTTP post request to my API endpoint, my API application crashes with a System.ArgumentException:
Microsoft.AspNetCore.Server.IIS.Core.IISHttpServer: Error: Connection ID "18374686481282236432", Request ID "80000011-0000-ff00-b63f-84710c7967bb": An unhandled exception was thrown by the application.
System.ArgumentException: The key '[omitted binary data]' is invalid JQuery syntax because it is missing a closing bracket. (Parameter 'key')
at Microsoft.AspNetCore.Mvc.ModelBinding.JQueryKeyValuePairNormalizer.NormalizeJQueryToMvc(StringBuilder builder, String key)
at Microsoft.AspNetCore.Mvc.ModelBinding.JQueryKeyValuePairNormalizer.GetValues(IEnumerable`1 originalValues, Int32 valueCount)
at Microsoft.AspNetCore.Mvc.ModelBinding.JQueryFormValueProviderFactory.AddValueProviderAsync(ValueProviderFactoryContext context)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.CreateAsync(ActionContext actionContext, IList`1 factories)
at Microsoft.AspNetCore.Mvc.ModelBinding.CompositeValueProvider.TryCreateAsync(ActionContext actionContext, IList`1 factories)
at Microsoft.AspNetCore.Mvc.Controllers.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<<CreateBinderDelegate>g__Bind|0>d.MoveNext()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Server.IIS.Core.IISHttpContextOfT`1.ProcessRequestAsync()
Microsoft.AspNetCore.Hosting.Diagnostics: Information: Request finished HTTP/1.1 POST http://localhost:10891/validation/dummy application/x-www-form-urlencoded 11072 - 500 - - 164.0024ms
The logs are showing that the correct route action is being used.
How do I disable automatic model binding for only this specific action handler?
Reminder: I cannot do any changes to the third-party application. I have to handle what I receive. I know the request content type is wrong. Please don't make any notes in that regard.
Edit: I have found the surface-level cause for this error. When I remove [FromRoute] string requestId from the function signature, the error will not occur. When I reintroduce it, the error occurs again.
Does not work (causes ASP.NET Core internal exception):
public async Task<IActionResult> Validation_Post([FromRoute] string requestId)
Does work:
public async Task<IActionResult> Validation_Post()
However, I need to access the route variable through Request.RouteValues["requestId"].
Anyway, the question still stands: How do I disable automatic model binding for only this specific action handler?