7

In process of migrating from .net framework and are struggling with a difference of response serialization.

Everything here seems to indicate that by default a response will go out via the json serializer. https://learn.microsoft.com/en-us/aspnet/core/web-api/advanced/formatting?view=aspnetcore-5.0 but when returning a simple string value all our endpoints have the content type of "text/plain" so the text isn't surrounded by quotes.

Given this basic controller:

[ApiController]
[Route("[controller]")]
public class SanityController : ControllerBase
{
    public SanityController()
    {
    }

    [HttpGet0]
    public string Get()
    {
        return "Where are the quotes?";
    }
}

And our Program.cs:

var builder = WebApplication.CreateBuilder(args);

...

// Tried this as well, not sure what the difference even is between this an AddJsonOptions, seemed to work the same.
//builder.Services.Configure<JsonOptions>(options =>
//{
//    options.SerializerOptions.IncludeFields = true;
//    options.SerializerOptions.Converters.Add(new JsonStringEnumConverter());
//    options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
//    options.SerializerOptions.PropertyNameCaseInsensitive = true;
//});

...

builder.Services.AddControllers()
    .AddJsonOptions(options =>
    {
        options.JsonSerializerOptions.IncludeFields = true;
        options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
        options.JsonSerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
        options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
    });

...

app.Run();

But the Content-Type returned for all of our endpoints that return strings is just "text/plain". We tried changing the Accept header on our client from application/json, text/plain, */* to application/json, text/plain;q=0.9, */*;q=0.8 but it would still return text. Only if we only set it to just application/json would it finally return json. But this doesn't help when hitting it directly from a browser window where it will again just return it as "text/plain".

We also found could also put [Produces("application/json")] on the controller method and that would also work.

But considering 99% of all our endpoints should return JSON with barely a handful returning text or xml, is there not a way to change the aggressive default for a string response to be application/json app wide like it was with asp.net?

3
  • How do you want your string to be converted as json? Commented Jun 20, 2023 at 22:42
  • Quotes. text/plaid returns Where are the quotes? where application/json returns "Where are the quotes". Commented Jun 20, 2023 at 22:59
  • Have you tried public IActionResult Get() { return Json("Where are the quotes?"); } - UNTESTED! Commented Jun 20, 2023 at 23:19

3 Answers 3

9

My approach:

string json = await httpResponseMessage.Content.ReadAsStringAsync();
return new ContentResult()
{
    Content = json,
    ContentType = "application/json"
};
Sign up to request clarification or add additional context in comments.

3 Comments

Nice. An alternative for the string literal for ContentType is to use the constant: System.Net.Mime.MediaTypeNames.Application.Json.
For a single return like that on a controller method I still find it cleaner to just flag it with the attribute [Produces(System.Net.Mime.MediaTypeNames.Application.Json)] and not mess around with ContentResult.
Thank you, this worked where nothing else had. For my case I needed another header and adding Response.Headers["blahblah"] = "blah"; before the return, worked.
3

You can remove StringOutputFormatter (if needed add it to the end with options.OutputFormatters.Add(new StringOutputFormatter()); after RemoveType call):

builder.Services.AddControllers(options =>
{
    options.OutputFormatters.RemoveType<StringOutputFormatter>();
});

Moving from the comments:

Also you can always use Json or Content helper methods to enforce the required output media type

For example something along these lines:

return Content(content: json, contentType: "application/json");

4 Comments

We wondered about this approach but since there were a couple instances where we did actually want a plain response we were hoping to find something better. But considering the number of cases is just a few compared to the everything else might be worth just making those few support being returned as json and call it a day if there's not way to keep both.
@Wrightboy try removing it and than adding it back as written in the answer, i.e. options.OutputFormatters.RemoveType<StringOutputFormatter>(); options.OutputFormatters.Add(new StringOutputFormatter());, this will add the string formatter back at the end of the pipeline and it should work when plain text is requested. Also you can always use Json or Content helper methods to enforce the required output media type.
Just to clarify, that doing what @GuruStron suggested, will change the order of precedence during content negotiation. So, the precedence will be for application/json instead of text/plain. Which is probably appropriate for your use case.
@GuruStron That's hilarious, and it worked perfect. Makes sense that it would allow the json formatter to move to the top spot. Thank you.
2

String value is a valid JSON, however it is confusing to ASP.Net core and it can't know if you intended it to be an application/json or a text/plain. What you can do in this case, is to specify that you need a application/json in the request levraging content negotation.

using the Accept to be application/json will make asp.net core return application/json instead of text/plain.

Postman screenshot And the proper header. Headers

Or else if you want to do it in a global manner, you can remove the plain/text formatter :

services.AddControllers(options => options.OutputFormatters.RemoveType<StringOutputFormatter>());

This, of course, will prevent you from never being able to send text/plain back. even if you set the Produces Attribute, or send an Accept header with text/plain.

3 Comments

String value without quotes is NOT valid JSON value (if we expecting to get string object, not integer, for example). From JSON documentation: "A value can be a string in double quotes, or a number, or true or false or null, or an object or an array. These structures can be nested." Also you can check this with jsonlint.com.
True, but in my screenshots they are around quotes.
I don't want to challenge the solution, I just told about main postulate. :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.