如何将纯文本发布到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"类型的对象。
实际上,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没有现成的格式化程序来处理文本/纯文本,因此一些选项:
-
将您的操作修改为没有参数。。。原因是参数触发了请求正文的反序列化。现在,您可以通过执行
await Request.Content.ReadAsStringAsync()
来显式读取请求内容,以获得字符串 -
编写一个自定义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");