135

I need to get a response back in plain text from a ASP.NET Web API controller.

I have tried do a request with Accept: text/plain but it doesn't seem to do the trick. Besides, the request is external and out of my control. What I would accomplish is to mimic the old ASP.NET way:

context.Response.ContentType = "text/plain";
context.Response.Write("some text);

Any ideas?

EDIT, solution: Based on Aliostad's answer, I added the WebAPIContrib text formatter, initialized it in the Application_Start:

  config.Formatters.Add(new PlainTextFormatter());

and my controller ended up something like:

[HttpGet, HttpPost]
public HttpResponseMessage GetPlainText()
{
  return ControllerContext.Request.CreateResponse(HttpStatusCode.OK, "Test data", "text/plain");
}
Magnus Johansson
  • 28,010
  • 19
  • 106
  • 164

6 Answers6

251

Hmmm... I don't think you need to create a custom formatter to make this work. Instead return the content like this:

    [HttpGet]
    public HttpResponseMessage HelloWorld()
    {
        string result = "Hello world! Time is: " + DateTime.Now;
        var resp = new HttpResponseMessage(HttpStatusCode.OK);
        resp.Content = new StringContent(result, System.Text.Encoding.UTF8, "text/plain");
        return resp;
    }

This works for me without using a custom formatter.

If you explicitly want to create output and override the default content negotiation based on Accept headers you won't want to use Request.CreateResponse() because it forces the mime type.

Instead explicitly create a new HttpResponseMessage and assign the content manually. The example above uses StringContent but there are quite a few other content classes available to return data from various .NET data types/structures.

Rick Strahl
  • 17,302
  • 14
  • 89
  • 134
  • 1
    This is in fact the solution I went for because my API would be returning JSON objects to 99% of all methods, only a few (very few) methods would need plain string responses (and for many of those I use a MemoryStream to return data directly in the response so it was a non-issue.) Only in 2 or 3 methods did I return a .NET string, and it was being returned as a JSON string. Your answer, IMHO, is the KISS response for this problem (although it is not 100% DRY, but I just wrote an extension method to string to do that... :-) Nice!) StringContent is very nice. Thank you. – Loudenvier Jun 15 '13 at 12:34
  • There are a number of custom XXXContent classes to create specific types of content that make this sort of thing pretty straight forward. – Rick Strahl Mar 20 '15 at 02:19
  • I see the correct response with this approach. However, HttpContext.Current is null now. Any ideas on this? – Nachiket Mehta May 12 '15 at 22:05
  • @JavascriptEnthusiast - HttpContext.Current is null most likely because you are self-hosting or running through the OWin stack without the System.Web pipeline. Unrelated to this solution though. – Rick Strahl Jul 05 '15 at 23:46
25

For .net core:

[HttpGet("About")]
public ContentResult About()
{
    return Content("About text");
}

https://learn.microsoft.com/en-us/aspnet/core/mvc/models/formatting

rook
  • 2,819
  • 4
  • 25
  • 41
15

If you are just looking for a simple plain/text formatter without adding additional dependencies, this should do the trick.

public class TextPlainFormatter : MediaTypeFormatter
{
    public TextPlainFormatter()
    {
        this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain"));
    }

    public override bool CanWriteType(Type type)
    {
        return type == typeof(string);
    }

    public override bool CanReadType(Type type)
    {
        return type == typeof(string);
    }

    public override Task WriteToStreamAsync(Type type, object value, Stream stream, HttpContentHeaders contentHeaders, TransportContext transportContext)
    {
        return Task.Factory.StartNew(() => {
            StreamWriter writer = new StreamWriter(stream);
            writer.Write(value);
            writer.Flush();
        });
    }

    public override Task<object> ReadFromStreamAsync(Type type, Stream stream, HttpContentHeaders contentHeaders, IFormatterLogger formatterLogger)
    {
        return Task.Factory.StartNew(() => {
            StreamReader reader = new StreamReader(stream);
            return (object)reader.ReadToEnd();
        });
    }
}

Don't forget to add it to your Global web api config.

config.Formatters.Add(new TextPlainFormatter());

Now you can pass string objects to

this.Request.CreateResponse(HttpStatusCode.OK, "some text", "text/plain");
Despertar
  • 21,627
  • 11
  • 81
  • 79
12
  • Please be careful not to use context in ASP.NET Web API or you will sooner or later be sorry. Asynchronous nature of ASP.NET Web API makes using HttpContext.Current a liability.
  • Use a plain text formatter and add to your formatters. There are dozens of them around. You could even write yours easily. WebApiContrib has one.
  • You can force it by setting the content type header on httpResponseMessage.Headers to text/plain in your controller provided you have registered plain text formatter.
Aliostad
  • 80,612
  • 21
  • 160
  • 208
  • Don't worry, I neither implied nor intended to use the HttpContext object, I just added it to illustrate how one would do it in classic ASP.NET – Magnus Johansson Jul 20 '12 at 15:29
  • Well, waddayknow, I already had WebAPIContrib referenced, sometimes it's simple. – Magnus Johansson Jul 20 '12 at 15:40
  • @Magnus Sure. I in fact changed the wording after I read what I had written. But reading another answer made me stress that first point. – Aliostad Jul 20 '12 at 15:44
  • You are saying not to use HttpContext.Current, what are the alternatives? – surya Aug 09 '13 at 13:35
  • @spiderdevil yes, it is absolutely what I am saying. You should not need it, pass request/response/configuration directly. – Aliostad Aug 09 '13 at 16:29
  • creating the `HttpResponseMessage` directly (per [this answer](http://stackoverflow.com/a/13028027/1037948)) means you don't need to create a plain text formatter – drzaus Sep 26 '13 at 19:00
  • per this answer http://stackoverflow.com/questions/24956178/using-httpcontext-current-in-webapi-is-dangerous-because-of-async it seems that HttpContext.Current is ok to use, even in async actions. – Phil Degenhardt Jan 26 '15 at 13:36
6

When Accept: text/plain doesnt work, then there is no registered formatter for text mime types.

You can ensure that there is no formatters for specified mime type by getting list of all supported formatters from service configuration.

Create a very straightforward media type formatter that support text mime types.

http://www.asp.net/web-api/overview/formats-and-model-binding/media-formatters

Regfor
  • 8,515
  • 1
  • 38
  • 51
1

An extension like the following one can reduce the number of lines and beautify your code:

public static class CommonExtensions
{
    public static HttpResponseMessage ToHttpResponseMessage(this string str)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new StringContent(str, System.Text.Encoding.UTF8, "text/plain")
        };

        return resp;
    }
}


Now you can consume the defined extension in your Web API:

public class HomeController : ApiController
{
    [System.Web.Http.HttpGet]
    public HttpResponseMessage Index()
    {
        return "Salam".ToHttpResponseMessage();
    }
}


By routing {DOMAIN}/api/Home/Index you can see the following plain text:

MyPlainTextResponse

Siyavash Hamdi
  • 2,764
  • 2
  • 21
  • 32