Czy istnieje sposób wymuszenia na interfejsie ASP.NET Web API zwracania zwykłego tekstu?

125

Potrzebuję odpowiedzi w postaci zwykłego tekstu z kontrolera interfejsu API sieci Web ASP.NET.

Próbowałem wysłać żądanie, Accept: text/plainale wydaje się, że nie działa. Poza tym prośba jest zewnętrzna i poza moją kontrolą. To, co chciałbym osiągnąć, to naśladować stary sposób ASP.NET:

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

Jakieś pomysły?

EDYCJA, rozwiązanie : Na podstawie odpowiedzi Aliostad dodałem program formatujący tekst WebAPIContrib , zainicjowałem go w Application_Start:

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

a mój kontroler zakończył się czymś takim:

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

Odpowiedzi:

231

Hmmm ... Myślę, że nie potrzebujesz tworzyć niestandardowego programu formatującego, aby to działało. Zamiast tego zwróć zawartość w następujący sposób:

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

Działa to dla mnie bez używania niestandardowego programu formatującego.

Jeśli jawnie chcesz utworzyć dane wyjściowe i przesłonić domyślną negocjację treści w oparciu o nagłówki Accept, nie będziesz chciał jej używać, Request.CreateResponse()ponieważ wymusza typ MIME.

Zamiast tego jawnie utwórz nowy HttpResponseMessagei przypisz zawartość ręcznie. W powyższym przykładzie użyto, StringContentale dostępnych jest kilka innych klas zawartości, które zwracają dane z różnych typów / struktur danych .NET.

Rick Strahl
źródło
1
Jest to w rzeczywistości rozwiązanie, które wybrałem, ponieważ moje API zwracałoby obiekty JSON do 99% wszystkich metod, tylko kilka (bardzo nielicznych) metod wymagałoby zwykłych odpowiedzi typu string (a dla wielu z nich używam MemoryStream do zwracania danych bezpośrednio w odpowiedzi, więc nie był to problem.) Tylko w 2 lub 3 metodach zwróciłem ciąg .NET i był zwracany jako ciąg JSON. Twoja odpowiedź, IMHO, jest odpowiedzią KISS na ten problem (chociaż nie jest to w 100% DRY, ale właśnie napisałem metodę rozszerzenia ciągu, aby to zrobić ... :-) Świetnie!) StringContent jest bardzo fajny. Dziękuję Ci.
Loudenvier
Istnieje wiele niestandardowych klas XXXContent do tworzenia określonych typów treści, które sprawiają, że tego rodzaju rzeczy są dość proste.
Rick Strahl
Widzę właściwą odpowiedź w takim podejściu. Jednak HttpContext.Current ma teraz wartość null. Jakieś pomysły na ten temat?
Nachiket Mehta
@JavascriptEnthusiast - HttpContext.Current ma najprawdopodobniej wartość null, ponieważ jest to serwer samoobsługowy lub działa przez stos OWin bez potoku System.Web. Jednak niezwiązane z tym rozwiązaniem.
Rick Strahl,
15

Jeśli szukasz prostego programu formatującego zwykły / tekstowy bez dodawania dodatkowych zależności, powinno to załatwić sprawę.

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

Nie zapomnij dodać go do konfiguracji globalnego interfejsu API sieci Web.

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

Teraz możesz przekazywać obiekty łańcuchowe do

this.Request.CreateResponse(HttpStatusCode.OK, "some text", "text/plain");
Despertar
źródło
12
  • Uważaj, aby nie używać kontekstu w ASP.NET Web API, bo prędzej czy później będziesz żałować. Asynchroniczny charakter interfejsu API sieci Web ASP.NET powoduje, że korzystanie HttpContext.Currentz odpowiedzialności.
  • Użyj programu formatującego zwykły tekst i dodaj do swoich elementów formatujących. Wokół jest ich dziesiątki. Możesz nawet łatwo napisać swoje. WebApiContrib ma jeden.
  • Można zmusić go, ustawiając typ zawartości nagłówka na httpResponseMessage.Headerscelu text/plainw kontrolerze o ile masz zarejestrowany zwykły formatowania tekstu.
Aliostad
źródło
Nie martw się, ja ani dorozumianych ani przeznaczone do użycia obiektu HttpContext, po prostu dodaje się go do zilustrowania, jak można by to zrobić w klasycznej ASP.NET
Magnus Johansson
Cóż, w dzisiejszych czasach miałem już odniesienie do WebAPIContrib, czasami jest to proste.
Magnus Johansson
@Magnus Sure. Faktycznie zmieniłem sformułowanie po przeczytaniu tego, co napisałem. Ale przeczytanie innej odpowiedzi sprawiło, że podkreśliłem pierwszy punkt.
Aliostad
Mówisz, aby nie używać HttpContext.Current, jakie są alternatywy?
surya,
@spiderdevil tak, dokładnie to mówię. Nie powinieneś tego potrzebować, prześlij żądanie / odpowiedź / konfigurację bezpośrednio.
Aliostad
6

Gdy Akceptuj: tekst / zwykły nie działa, nie ma zarejestrowanego programu formatującego dla typów MIME dla tekstu.

Możesz upewnić się, że nie ma elementów formatujących dla określonego typu MIME, pobierając listę wszystkich obsługiwanych elementów formatujących z konfiguracji usługi.

Utwórz bardzo prosty program do formatowania typów multimediów, który obsługuje tekstowe typy MIME.

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

Regfor
źródło
Szkoda, że ​​nie mogłem zaakceptować również twojej odpowiedzi, zaakceptowana odpowiedź zaoszczędziła mi kłopotów z pisaniem własnego programu formatującego. Co najmniej +1.
Magnus Johansson
0

Rozszerzenie, takie jak poniższe, może zmniejszyć liczbę wierszy i upiększyć kod:

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


Teraz możesz wykorzystać zdefiniowane rozszerzenie w Web API:

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


Po routingu {DOMAIN}/api/Home/Indexmożesz zobaczyć następujący zwykły tekst:

MyPlainTextResponse

Siyavash Hamdi
źródło
Nie marnuj przestrzeni nazw łańcuchów na rzeczy niezwiązane z ciągiem.
Rambalac