In this post, I showed how to modify the request body to add an additional field using middleware. It would also add a request body if not provided. However, if you send a request without request body, you would get
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.5.16",
"title": "Unsupported Media Type",
"status": 415,
"traceId": "00-92b30b239eca44beb048a4923d145cfc-4a150c0e97824d78-00"
}
or if the request has Content-Type in the headers but without request body, you would get this error
{
"type": "https://datatracker.ietf.org/doc/html/rfc9110#section-15.5.1",
"title": "One or more validation errors occurred.",
"status": 400,
"traceId": "00-ded02a7403fd42ca8880e92f6963fc10-5ec659fe255b420e-00",
"errors": {
"": [
"A non-empty request body is required."
]
}
}
You can allow empty request body and bypass this error using the method in my last post. However, .NET still wouldn’t bind to the model in the controller. It seems like it had determined that there is no request body before the custom middleware injecting a request body and so it does not even attempt to bind to the model.
After a bit of research, it was the case. See this and this.
To set CanHaveBody
to true from that link to tell .NET there is actually a request body, this is what I add to the custom middleware mentioned in the post at the start of this post.
public class CustomiseRequestBodyMiddleware
{
private readonly RequestDelegate _next;
public CustomiseRequestBodyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task Invoke(HttpContext context)
{
if (context.Request.Method == HttpMethods.Get ||
context.Request.Method == HttpMethods.Delete)
{
await _next(context);
return;
}
context.Request.EnableBuffering();
var bodyAsText = await new StreamReader(context.Request.Body).ReadToEndAsync();
if (string.IsNullOrWhiteSpace(bodyAsText))
{
bodyAsText = "{}";
}
var jsonPayload = JObject.Parse(bodyAsText);
jsonPayload.Remove("AdditionalField");
jsonPayload.Remove("additionalField");
jsonPayload.AddFirst(new JProperty(
"AdditionalField",
JObject.FromObject(new { NewField = "I'm a new field" })));
context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(jsonPayload)));
context.Request.ContentLength = context.Request.Body.Length;
if(!context.Features.GetRequiredFeature<IHttpRequestBodyDetectionFeature>().CanHaveBody)
{
context.Features.Set((IHttpRequestBodyDetectionFeature) new RequestBodyDetectionFeature(true));
}
await _next(context);
}
}
internal sealed class RequestBodyDetectionFeature(bool canHaveBody) : IHttpRequestBodyDetectionFeature
{
public bool CanHaveBody { get; } = canHaveBody;
}
Now the custom request body will be bound to the model in the controller when request has no request body.