I'm having trouble developing a C# (.Net 5) method on an API controller who can manage request who have 'multipart/form-data; boundary=boundary'
I can't change the request on the client side, I only can modify my backend to adapt to it.
The request specify the 'Content-Type: multipart/form-data; boundary=boundary', and it has a json message inside the body that I need to extract.
Inside the body of the request the message is like this:
--boundary
Content-Disposition: form-data; name="ZZZZZZZZZZ"
Content-Type: application/json
Content-Length: 474
{
//a well formed json
}
--boundary--
I can get that using this code: string dataMessage = new StreamReader(Request.Body).ReadToEndAsync().Result;
Then I can manually parse that string to extract only the json part using IndexOf and Substring, and then cast it to an object using JsonConvert.
I'm wondering if there is some option on .Net to do this automatically without the manual parsing of the string. I think that there must be some way to interpret the boundary token and get the json data directly. But i can't found any :( That came to my mind because when using webhook.site to test the client request, it can parse the message without any problems.
My controller definition is like this
[ApiController]
[Route("api/[controller]")]
public class XXXXController : ControllerBase
And the method is like this
[HttpPost]
[Route("[action]")]
public IActionResult YYYYYYY()
I already tried to specify the model binding to change the behaviour but I can't get it working.
And I already search help before asking:
Thanks for your help!!
There isn't built-in support of multi-part/form-data
Media Type in .Net5 by default. So, the input formatter should be attached to the MvcBuilder
.
Considering the fact that you can't manipulate client-side, ApiMultipartFormDataFormatter which enables this type of input formatting can be utilized.
Install-Package ApiMultipartFormDataFormatter -Version 3.0.0
Configure the Formatter in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(options =>
{
options.InputFormatters.Add(new MultipartFormDataFormatter());
});
}
Assuming the mentioned well formed json is something like below class:
public class CustomContent
{
public string Name { get; set; }
}
So, we need to wrap it up into another class that contains this as a parameter. Note: The library doesn't support deserialization by this moment So, a getter-only property is added to the model which returns the deserialized object.
public class ViewModel
{
// Equivalent to ZZZZZZZZZZ
public string SerializedContent { get; set; }
public CustomContent Content => !string.IsNullOrEmpty(SerializedContent)
? JsonConvert.DeserializeObject<CustomContent>(SerializedContent)
: null;
}
Reform the Action to accept the wrapper model.
[HttpPost]
[Route("[action]")]
public IActionResult Parse(ViewModel vm)
{
return Ok($"Received Name: {vm?.Content?.Name}");
}
The working cURL request of the corresponding example is:
curl --location --request POST 'http://localhost:25599/api/MultiPart/Parse'
\
--header 'Content-Type: multipart/form-data; boundary=boundary' \
--data-raw '--boundary
Content-Disposition: form-data; name="SerializedContent"
Content-Type: application/json
Content-Length: 100
{
"Name" : "Foo"
}
--boundary--'
The response should be equal to Received Name: Foo
.