ASP.NET Core Pitfalls – Posting a String

This is another post on my ASP.NET Core pitfalls series. It is actually related to this one ASP.NET Core Pitfalls – Null Models in Post Requests.

What happens if you try to submit a string containing JSON as a POST? I mean, you always submit strings, but this time you don't want for it to be parsed into some class. Like this:
[HttpPost]
public IActionResult PostString([FromBody] string json) { ... }

You may be surprised to find out that this fails with a HTTP 415 Unsupported Media Type.

Now, you might be tempted to add an [Consumes] attribute to it, such as:

[HttpPost]
[Consumes("application/json")]
public IActionResult PostString([FromBody] string json) { ... }

And this will actually work! The problem is, or could be, that it requires the sender to send the appropriate Content-Type header ("application/json").

Another, better, solution is to just read the string from the from the request stream:

[HttpPost]
public IActionResult PostString()
{
    using var reader = new StreamReader(HttpContext.Request.Body));
    var json = reader.ReadToEnd();
    ...
}

Of course, this could be made asynchronous, as StreamReader has a ReadToEndAsync method.

Yet another option is to add a custom input formatter, a class that implements IInputFormatter, one that just accepts a string, which needs to be registered globally:

builder.Services.AddControllersWithViews(static options =>
{
    options.InputFormatters.Insert(0, new StringInputFormatter());
});

One example would be:

internal class StringInputFormatter : IInputFormatter
{
    public bool CanRead(InputFormatterContext context)
    {
        var canRead = context.HttpContext.Request.Method == HttpMethods.Post 
            && context.HttpContext.Request.ContentLength > 0;
        return canRead;
    }

    public async Task<InputFormatterResult> ReadAsync(InputFormatterContext context)
    {
        using var reader = new StreamReader(context.HttpContext.Request.Body);
        var content = await reader.ReadToEndAsync();
        return await InputFormatterResult.SuccessAsync(content);
    }
}

As you can see that there's a lot more involved than with the other two solutions.

Hope this comes useful!

Comments

Popular posts from this blog

Domain Events with .NET

Domain Events with .NET - New Features

Named HttpClient Registrations