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

本文关键字:NET ASP Web API 端点 文本 | 更新日期: 2023-09-27 18:20:20

我有一个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"类型的对象。

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

实际上,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());

由于Web API没有现成的格式化程序来处理文本/纯文本,因此一些选项:

  1. 将您的操作修改为没有参数。。。原因是参数触发了请求正文的反序列化。现在,您可以通过执行await Request.Content.ReadAsStringAsync()来显式读取请求内容,以获得字符串

  2. 编写一个自定义MediaTypeFormatter来处理"text/plain"。。。实际上,在这种情况下编写非常简单,并且可以保留操作中的参数。

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

using (var reader = new StreamReader(Request.Body))
{
      string plainText= reader.ReadToEnd();
      // Do something else
      return Ok(plainText);
}

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

请注意,我有意不处理StreamReader/StreamWriter,因为这将处理底层流并中断Web Api流。请参阅此处:

"此方法的实现不应在完成时关闭readStream。当HttpContent实例被释放时,流将被独立关闭"

要使用它,请在构建HttpConfiguration:时进行注册

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

在某些情况下,让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" ) );

很晚才提出一个非常简化的解决方案。我在控制器方法中成功地使用了这个代码:

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

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

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

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