I am building an API in C#.Net using Controllers and utilising Swagger but I have the need for accepting an arbitrary set of key-values and cant find out how to get swagger to show an input box (and if possible, for the HttpPost method to split the body into an IFormCollection).
The POST content looks like the following:
key1=val1&key2=val2&key3=val3a&key3=val3b&.....
and so on to any number of key-values.
The handler looks like the following which works but SwaggerUI doesnt offer any input box to pass in the body:
[HttpPost]
[Consumes(MediaTypeNames.Application.FormUrlEncoded)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
public async Task<IActionResult> SetKeys()
{
var keyvals = await Request.Body.ToDictionary(); // Uses Stream extension
if (keyvals.Count == 0)
return BadRequest("No data received");
// update database
return NoContent(); //204
}
I’ve tried different signatures all to no avail
public async Task<IActionResult> SetKeys([FromBody] string content)
public async Task<IActionResult> SetKeys([FromForm] string content)
public async Task<IActionResult> SetKeys([FromBody] string content)
public async Task<IActionResult> SetKeys([FromForm] IFormCollection content)
public async Task<IActionResult> SetKeys([FromBody] IFormCollection content)
public async Task<IActionResult> SetKeys([FromBody] List<KeyValuePair<string, string>> content)
Setting the content type to text/plain works, then reading [FromBody], but I still have to extract the body to key-value pairs and im loosing a validation check on content-type:
[HttpPost]
[Consumes(MediaTypeNames.Text.Plain)]
[ProducesResponseType(typeof(string), StatusCodes.Status200OK, MediaTypeNames.Application.FormUrlEncoded)]
public async Task<IActionResult> SetKeys([FromBody] string content)
{
var keyvals = await content.ToDictionary(); // Uses String extension
if (keyvals.Count == 0)
return BadRequest("No data received");
// update database
return NoContent(); //204
}
I even tried an InputFormatter which allows me to at least chose the Content-Type in swagger but it splits the content per character into a key:
using System.Net.Mime;
using Microsoft.AspNetCore.Mvc.Formatters;
namespace RequestRouter.Utilities;
public class RawBodyInputFormatter : InputFormatter
{
public RawBodyInputFormatter()
{
this.SupportedMediaTypes.Add(MediaTypeNames.Application.FormUrlEncoded);
//this.SupportedMediaTypes.Add(MediaTypeNames.Text.Plain);
}
public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
{
var request = context.HttpContext.Request;
using var reader = new StreamReader(request.Body);
var content = await reader.ReadToEndAsync();
return await InputFormatterResult.SuccessAsync(content);
}
protected override bool CanReadType(Type type)
{
return type == typeof(string);
}
}
How Swagger formats the curl when application/x-www-form-urlencoded is selected:
curl -X 'POST'
'http://localhost:8001/config'
-H 'accept: */*'
-H 'Content-Type: application/x-www-form-urlencoded'
-d '0=k&1=e&2=y&3=1&4=%3D&5=v&6=a&7=l&8=1'