2014-09-02 50 views
23

我有一個控制器動作的ASP.NET Web API端點定義如下:如何將純文本發佈到ASP.NET Web API端點?

[HttpPost] 
public HttpResponseMessage Post([FromBody] object text) 

如果我的崗位要求的身體包含純文本(即不應該被解釋爲JSON,XML,或任何其他特殊格式) ,那麼我想我可能只是包括下面的頭我的要求:

Content-Type: text/plain 

不過,我收到錯誤:

No MediaTypeFormatter is available to read an object of type 'Object' from content with media type 'text/plain'. 

如果我改變我的控制器操作方法簽名:

[HttpPost] 
public HttpResponseMessage Post([FromBody] string text) 

我得到一個稍微不同的錯誤信息:

No MediaTypeFormatter is available to read an object of type 'String' from content with media type 'text/plain'. 
+0

你不想你的「文字」參數是一個字符串類型的呢? – 2014-09-02 20:43:12

+0

是的,我也想到了這個想法,但它沒有多大幫助(請看我修改的問題) – BaltoStar 2014-09-02 21:37:56

+0

@BaltoStar我知道你的問題是8個月大,但我面臨同樣的問題。請參閱下面的答案。 – gwenzek 2015-04-28 08:38:29

回答

9

由於網絡API沒有開箱格式化處理text/plain的,一些選項:

  1. 修改您的操作沒有參數...原因是有參數觸發請求身體反序列化。現在,您可以通過執行「await Request.Content.ReadAsStringAsync()」來顯式讀取請求內容以獲取字符串

  2. 編寫自定義mediatypeformatter以處理'text/plain'...它實際上很簡單情況下,你可以保持參數的行動。

+0

感謝Kiran的迴應。實際上,我的動作簽名還有其他[FromUri]參數,我忽略了這些參數以簡化問題。所以我想這意味着我無法避免請求體的隱式反序列化?在這種情況下,我需要編寫一個自定義的MediaTypeFormatter來處理'text/plain'... – BaltoStar 2014-09-02 23:52:51

+0

請求反序列化只會發生在通常被認爲是從body讀取的參數上......例子:顯式地''FromBody ]'裝飾參數,從身體隱含地讀取(例如:複雜類型)...所以我上面的答案只是爲了從身體讀取的那些...所以你應該能夠像往常一樣使用'FromUri'參數...但無論如何,創建自定義格式化程序是最好的方法,因爲它非常適合Web API設計。 – 2014-09-03 11:12:25

41

實際上,對於純文本而言,Web API沒有MediaTypeFormatter是一種遺憾。這是我實施的一個。它也可以用來發布內容。

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

    public override Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) 
    { 
     var taskCompletionSource = new TaskCompletionSource<object>(); 
     try 
     { 
      var memoryStream = new MemoryStream(); 
      readStream.CopyTo(memoryStream); 
      var s = System.Text.Encoding.UTF8.GetString(memoryStream.ToArray()); 
      taskCompletionSource.SetResult(s); 
     } 
     catch (Exception e) 
     { 
      taskCompletionSource.SetException(e); 
     } 
     return taskCompletionSource.Task; 
    } 

    public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, System.Net.TransportContext transportContext, System.Threading.CancellationToken cancellationToken) 
    { 
     var buff = System.Text.Encoding.UTF8.GetBytes(value.ToString()); 
     return writeStream.WriteAsync(buff, 0, buff.Length, cancellationToken); 
    } 

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

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

您需要通過類似的東西在你的HttpConfig以「註冊」此格式:

config.Formatters.Insert(0, new TextMediaTypeFormatter()); 
+0

正是我需要的!好東西! – 2015-05-30 10:05:35

+0

太棒了!這是一個很棒的解決方案! – ThatAwesomeCoder 2016-11-21 06:20:33

3

在某些情況下,它可能是簡單的讓JsonMediaTypeFormatter讓做的工作:

var formatter = GlobalConfiguration.Configuration.Formatters.Where(f=>f is System.Net.Http.Formatting.JsonMediaTypeFormatter).FirstOrDefault(); 
if (!formatter.SupportedMediaTypes.Any(mt => mt.MediaType == "text/plain")) 
    formatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/plain")); 
+0

如果我這樣做,然後像這樣設置主體:'resp.Content = new StringContent(outputString,Encoding.UTF8,「text \ plain」);',它會直接輸出字符串(即使它是而不是JSON)與純文本內容類型? – 2017-02-26 02:50:51

2

gwenzek的格式化程序使用async/await的純化版本:

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

    public override bool CanReadType(Type type) => 
     type == typeof(string); 

    public override bool CanWriteType(Type type) => 
     type == typeof(string); 

    public override async Task<object> ReadFromStreamAsync(Type type, Stream readStream, HttpContent content, IFormatterLogger formatterLogger) 
    { 
     var streamReader = new StreamReader(readStream); 
     return await streamReader.ReadToEndAsync(); 
    } 

    public override async Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext, CancellationToken cancellationToken) 
    { 
     var streamReader = new StreamWriter(writeStream); 
     await streamReader.WriteAsync((string) value); 
    } 
} 

請注意I 有意不要配置 StreamReader/StreamWriter,因爲這樣會處理底層流並中斷Web Api流。

要使用它,登記,同時建立HttpConfiguration:

protected HttpConfiguration CreateHttpConfiguration() 
{ 
    HttpConfiguration httpConfiguration = new HttpConfiguration(); 
    ... 
    httpConfiguration.Formatters.Add(new PlainTextFormatter()); 
    ... 
    return httpConfiguration; 
} 
相關問題