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

Audit Trails in EF Core

.NET Cancellation Tokens

Domain Events with .NET