How to receive a request with text/plain content in ASP.NET Core

By FoxLearn 3/1/2025 3:08:53 AM   204
When a request is received by your ASP.NET Core application and the action method has parameters, the framework tries to find an appropriate InputFormatter to deserialize the request body.

However, ASP.NET Core does not provide a built-in text/plain InputFormatter. As a result, if you send a request with text/plain content, it will fail with a 415 – Unsupported Media Type error.

In this article, I’ll show you how to implement a custom InputFormatter for handling text/plain content and how to configure your action method to process it.

Note: While an alternative approach could involve manually reading the Request.Body using a StreamReader in the action method, I recommend using the InputFormatter approach for cleaner and more maintainable code.

Implement a Custom InputFormatter for text/plain

For example, how to create a custom InputFormatter to handle text/plain requests.

using Microsoft.AspNetCore.Mvc.Formatters;

public class TextSingleValueFormatter : InputFormatter
{
    private const string TextPlain = "text/plain";        
    
    public TextSingleValueFormatter()
    {
        SupportedMediaTypes.Add(TextPlain);
    }
    
    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context)
    {
        try
        {
            using (var reader = new StreamReader(context.HttpContext.Request.Body))
            {
                string textSingleValue = await reader.ReadToEndAsync();
                // Convert from string to the parameter's target type
                object model = Convert.ChangeType(textSingleValue, context.ModelType);
                return InputFormatterResult.Success(model);
            }
        }
        catch(Exception ex)
        {
            context.ModelState.TryAddModelError("BodyTextValue", $"{ex.Message} ModelType={context.ModelType}");
            return InputFormatterResult.Failure();
        }
    }

    protected override bool CanReadType(Type type)
    {
        // Define the types the formatter should handle
        return type == typeof(string) ||
               type == typeof(int) ||
               type == typeof(DateTime);
    }
    
    public override bool CanRead(InputFormatterContext context)
    {
        return context.HttpContext.Request.ContentType == TextPlain;
    }
}

This formatter reads the request body as a string, converts it to the target model type (the type of the parameter in the action method), and handles errors by adding them to the ModelState. If an error occurs, the formatter returns a failure response, and the framework responds with a standard "problem details" error.

Register the Custom InputFormatter

Next, you need to register your custom InputFormatter within the application setup.

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddControllers(mvcOptions =>
{
    mvcOptions.InputFormatters.Add(new TextSingleValueFormatter());
});

// The rest of your initialization code

Use [FromBody] for the Parameter

To bind the incoming data from the request body to a parameter, use the [FromBody] attribute on the parameter.

[HttpPost()]
[Consumes("text/plain")]
public IActionResult Post([FromBody] string name)
{
    return Ok($"Posted value={name}");
}

Note: You can optionally use the [Consumes("text/plain")] attribute on the action method to restrict it to only accept text/plain content.

Once the custom InputFormatter is registered and your action method is configured, you can now send requests with text/plain content.

For example, Send a String

Send a POST request with a string in the body and set the Content-Type to text/plain:

POST /Example
Content-Type: text/plain

Bob

This will return a 200 OK response with:

Posted value Bob

For example, Send an Integer

The custom TextSingleValueFormatter will look at the parameter type and try to convert the body content to that type.

For example, How to receive an int from a text/plain request:

[HttpPost()]
[Consumes("text/plain")]
public IActionResult Post([FromBody] int age)
{
    return Ok($"Posted age={age}");
}

Now, send the following request:

POST /Example
Content-Type: text/plain

1

This returns a 200 OK response with:

Posted age=1

For example, Handle Errors

If the data in the request body doesn’t match the expected type (for example, sending a string when an integer is expected), the custom InputFormatter will add error details to the ModelState. This results in a standard "problem details" error response.

For instance, if the request body contains invalid data, such as:

POST /Example
Content-Type: text/plain

not an int

You will receive the following 400 Bad Request response:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.1",
    "title": "One or more validation errors occurred.",
    "status": 400,
    "traceId": "00-7d905c800640e2770c1dffcf28d89586-6ef5d985832717f5-00",
    "errors": {
        "BodyTextValue": [
            "Input string was not in a correct format. ModelType=System.Int32"
        ]
    }
}

By creating a custom InputFormatter for text/plain content, you can easily handle requests with this content type and deserialize them into the appropriate model types.