¿Hay alguna manera de obligar a la API web de ASP.NET a devolver texto sin formato?

125

Necesito obtener una respuesta en texto plano de un controlador de API web ASP.NET.

He intentado hacer una solicitud Accept: text/plainpero no parece funcionar. Además, la solicitud es externa y está fuera de mi control. Lo que lograría es imitar la antigua forma ASP.NET:

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

¿Algunas ideas?

EDITAR, solución : basado en la respuesta de Aliostad, agregué el formateador de texto WebAPIContrib , lo inicialicé en Application_Start:

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

y mi controlador terminó algo así como:

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

Respuestas:

231

Hmmm ... no creo que necesites crear un formateador personalizado para que esto funcione. En su lugar, devuelva el contenido de esta manera:

    [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;
    }

Esto funciona para mí sin usar un formateador personalizado.

Si desea crear explícitamente una salida y anular la negociación de contenido predeterminada basada en los encabezados Aceptar, no querrá usar Request.CreateResponse()porque fuerza el tipo MIME.

En su lugar, cree explícitamente uno nuevo HttpResponseMessagey asigne el contenido manualmente. El ejemplo anterior usa, StringContentpero hay bastantes otras clases de contenido disponibles para devolver datos de varios tipos / estructuras de datos .NET.

Rick Strahl
fuente
1
De hecho, esta es la solución que busqué porque mi API devolvería objetos JSON al 99% de todos los métodos, solo unos pocos (muy pocos) métodos necesitarían respuestas de cadena simple (y para muchos de ellos uso un MemoryStream para devolver datos directamente en la respuesta, por lo que no fue un problema.) Solo en 2 o 3 métodos devolví una cadena .NET, y se devolvió como una cadena JSON. Su respuesta, en mi humilde opinión, es la respuesta de KISS para este problema (aunque no es 100% SECO, pero acabo de escribir un método de extensión para encadenar para hacer eso ... :-) ¡Qué bueno!) StringContent es muy bueno. Gracias.
Loudenvier
Hay una serie de clases personalizadas de XXXContent para crear tipos específicos de contenido que hacen que este tipo de cosas sea bastante sencillo.
Rick Strahl el
Veo la respuesta correcta con este enfoque. Sin embargo, HttpContext.Current es nulo ahora. ¿Alguna idea sobre esto?
Nachiket Mehta
@JavascriptEnthusiast: HttpContext.Current es probablemente nulo porque se autohospeda o ejecuta a través de la pila OWin sin la canalización System.Web. Sin embargo, no está relacionado con esta solución.
Rick Strahl el
15

Si solo está buscando un formateador simple de texto plano sin agregar dependencias adicionales, esto debería ser el truco.

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();
        });
    }
}

No olvide agregarlo a su configuración global de API web.

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

Ahora puede pasar objetos de cadena a

this.Request.CreateResponse(HttpStatusCode.OK, "some text", "text/plain");
Despertar
fuente
12
  • Tenga cuidado de no usar el contexto en la API web de ASP.NET o tarde o temprano lo lamentará. La naturaleza asincrónica de la API web ASP.NET hace que el uso de HttpContext.Currentuna responsabilidad.
  • Use un formateador de texto plano y agréguelo a sus formateadores. Hay docenas de ellos alrededor. Incluso podrías escribir el tuyo fácilmente. WebApiContrib tiene uno.
  • Puede forzar mediante el establecimiento de la cabecera de tipo de contenido en el httpResponseMessage.Headersque text/plainen su controlador y cuando hayan registrado formateador de texto sin formato.
Aliostad
fuente
No se preocupe, no implicaba ni tenía la intención de usar el objeto HttpContext, solo lo agregué para ilustrar cómo se haría en ASP.NET clásico
Magnus Johansson
Bueno, waddayknow, ya tenía referenciada WebAPIContrib, a veces es simple.
Magnus Johansson
@Magnus seguro. De hecho, cambié la redacción después de leer lo que había escrito. Pero leer otra respuesta me hizo enfatizar ese primer punto.
Aliostad
Está diciendo que no use HttpContext.Current, ¿cuáles son las alternativas?
surya
@ spiderdevil sí, es absolutamente lo que estoy diciendo. No debería necesitarlo, pasar solicitud / respuesta / configuración directamente.
Aliostad
6

Cuando Aceptar: text / plain no funciona, entonces no hay un formateador registrado para los tipos de mime de texto.

Puede asegurarse de que no haya formateadores para el tipo MIME especificado al obtener una lista de todos los formateadores compatibles de la configuración del servicio.

Cree un formateador de tipos de medios muy sencillo que admita tipos de mime de texto.

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

Regfor
fuente
Ojalá pudiera aceptar tu respuesta también, la respuesta aceptada me ahorró la molestia de escribir mi propio formateador. +1 al menos.
Magnus Johansson
0

Una extensión como la siguiente puede reducir la cantidad de líneas y embellecer su código:

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;
    }
}


Ahora puede consumir la extensión definida en su Web API:

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


Al enrutar {DOMAIN}/api/Home/Indexpuede ver el siguiente texto sin formato:

MyPlainTextResponse

Siyavash Hamdi
fuente
No desperdicie el espacio de nombres de la cadena con cosas no relacionadas con la cadena.
Rambalac