如何将纯文本发送到 ASP.NET Web API 端点?

问题描述 投票:0回答:8

我有一个 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)

我收到了略有不同的错误消息:

没有 MediaTypeFormatter 可用于从媒体类型为“text/plain”的内容中读取“String”类型的对象。

c# asp.net-web-api content-type
8个回答
74
投票

实际上,遗憾的是 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());

24
投票

由于 Web API 没有用于处理文本/纯文本的开箱即用格式化程序,因此有一些选项:

  1. 将您的操作修改为没有参数...原因是有参数会触发请求正文反序列化。现在您可以通过执行

    await Request.Content.ReadAsStringAsync()
    来获取字符串

  2. 来显式读取请求内容
  3. 编写一个自定义 MediaTypeFormatter 来处理“文本/纯文本”...在这种情况下编写实际上很简单,您可以将参数保留在操作上。


22
投票

在 ASP.NET Core 2.0 中,您只需执行以下操作:-

using (var reader = new StreamReader(Request.Body))
{
      string plainText= reader.ReadToEnd();

      // Do something else

      return Ok(plainText);
}

17
投票

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

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

请注意,我故意不处置 StreamReader/StreamWriter,因为这将处置底层流并破坏 Web Api 流程。请参阅此处

“此方法的实现不应在完成后关闭 readStream。当 HttpContent 实例被处置时,该流将独立关闭。”

要使用它,请在构建时注册

HttpConfiguration

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

6
投票

在某些情况下,让 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
投票

这个聚会已经很晚了,解决方案非常简化。 我在控制器方法中成功使用了此代码:

       public HttpResponseMessage FileHandler()
       {
        HttpResponseMessage response = new HttpResponseMessage();

        using (var reader = new StreamReader(System.Web.HttpContext.Current.Request.GetBufferedInputStream()))
        {
            string plainText = reader.ReadToEnd();
        } .....}

在客户端,这些是我使用的 Ajax 选项:

var ajaxOptions = {
url: 'api/fileupload/' + "?" + $.param({ "key": metaKey2, "File-Operation": "Remove", "removalFilePath": $removalFilePath, "Audit-Model": model, "Parent-Id": $parentId, "Audit-Id": $auditId }),
type: 'POST', processData: false, contentType: false, data: "BOB"
};


0
投票

这不是一个正确的答案,而是一个快速但肮脏的解决方法来解锁开发......

事实证明,用引号分隔的字符串本身就是有效的 JSON。因此,如果您确定内容总是非常简单,则可以将其用双引号括起来,并将其命名为 application/json。

// TODO: Temporary, fix for production
HttpContent content = new StringContent($"\"{command}\"", UTF8Encoding.UTF8, "application/json");

0
投票

.NET 5+ 起,格式化程序已分为

TextInputFormatter
TextOutputFormatter
。因此,支持纯文本作为正文内容的结果类略有变化:

public class PlainTextFormatter : TextInputFormatter
{
    public PlainTextFormatter()
    {
        SupportedMediaTypes.Add(new MediaTypeHeaderValue(MediaTypeNames.Text.Plain));

        SupportedEncodings.Add(Encoding.UTF8);
        SupportedEncodings.Add(Encoding.ASCII);
    }

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

    public override async Task<InputFormatterResult> ReadRequestBodyAsync(InputFormatterContext context, Encoding encoding)
    {
        var reader = new StreamReader(context.HttpContext.Request.Body);
        var plainText = await reader.ReadToEndAsync();
        
        return await InputFormatterResult.SuccessAsync(plainText);
    }
}

Startup.cs
Program.cs
注册:

services.AddControllers(options => options.InputFormatters.Add(new PlainTextFormatter()))

并确保通过

Content-Type: text/plain
发送您的请求。

© www.soinside.com 2019 - 2024. All rights reserved.