JToken.WriteToAsync 不写入 JsonWriter [英] JToken.WriteToAsync does not write to JsonWriter
问题描述
我正在尝试创建一个以某种方式更改请求的中间件.我能够阅读它并更改内容,但我无法弄清楚如何正确设置流编写器以创建新主体.当我调用 normalized.WriteToAsync(jsonWriter)
时,MemoryStream
保持为空,因此我收到 A non-empty request body is required. 异常.我在这里错过了什么?这是我目前所拥有的:
I'm trying to create a middleware that changes the request in a certain way. I am able to read it and change the content but I cannot figure out how to correctly setup the stream writers to create a new body. When I call normalized.WriteToAsync(jsonWriter)
the MemoryStream
remains empty and consequently I receive the A non-empty request body is required. exception. What am I missing here? This is what I have so far:
public async Task Invoke(HttpContext context)
{
if (context.Request.ContentType == "application/json" && context.Request.ContentLength > 0)
{
using var scope = _logger.BeginScope("NormalizeJson");
try
{
using var requestReader = new HttpRequestStreamReader(context.Request.Body, Encoding.UTF8);
using var jsonReader = new JsonTextReader(requestReader);
var json = await JToken.LoadAsync(jsonReader);
var normalized = _normalize.Visit(json); // <-- Modify json and return JToken
// Create new Body
var memoryStream = new MemoryStream();
var requestWriter = new StreamWriter(memoryStream);
var jsonWriter = new JsonTextWriter(requestWriter);
await normalized.WriteToAsync(jsonWriter); // <-- At this point the MemoryStream has still 0 length.
var content = new StreamContent(memoryStream.Rewind()); // <-- Use helper extension to Seek.Begin = 0
context.Request.Body = await content.ReadAsStreamAsync();
}
catch (Exception e)
{
_logger.Scope().Exceptions.Push(e);
}
}
await _next(context);
}
<小时>
演示,用于 LINQPad 等:
Demo for LINQPad etc.:
async Task Main()
{
var token = JToken.FromObject(new User { Name = "Bob" });
var memoryStream = new MemoryStream();
var requestWriter = new StreamWriter(memoryStream);
var jsonWriter = new JsonTextWriter(requestWriter);
await token.WriteToAsync(jsonWriter);
memoryStream.Length.Dump(); // <-- MemoryStream.Length = 0
}
public class User
{
public string Name { get; set; }
}
推荐答案
您需要正确刷新和关闭 JsonTextWriter
和 StreamWriter
以完全填充 memoryStream
,像这样:
You need to properly flush and close your JsonTextWriter
and StreamWriter
in order to fully populate the memoryStream
, like so:
var memoryStream = new MemoryStream();
// StreamWriter implements IAsyncDisposable
// Leave the underlying stream open
await using (var requestWriter = new StreamWriter(memoryStream, leaveOpen: true))
{
var jsonWriter = new JsonTextWriter(requestWriter); // But JsonTextWriter does not implement IAsyncDisposable, only IDisposable!
try
{
await token.WriteToAsync(jsonWriter);
}
finally
{
await jsonWriter.CloseAsync();
}
}
演示小提琴 #1 此处.
或者,由于您正在写入 MemoryStream
,因此根本不需要使用 async
,您可以这样做:
Or, since you're writing to a MemoryStream
, there's really no nead to use async
at all, and instead you can do:
var memoryStream = new MemoryStream();
using (var requestWriter = new StreamWriter(memoryStream, leaveOpen: true)) // Leave the underlying stream open
using (var jsonWriter = new JsonTextWriter(requestWriter))
{
token.WriteTo(jsonWriter);
}
演示小提琴 #2 此处.
注意事项:
注意
await using
用于StreamWriter代码>.此语法保证
StreamWriter
将异步刷新和关闭,并可用于任何实现IAsyncDisposable
.(这仅在您写入文件流或其他非内存流时才真正重要.)
Note the use of
await using
for theStreamWriter
. This syntax guarantees that theStreamWriter
will be flushed and closed asynchronously, and can be used on any object that implementsIAsyncDisposable
. (This only really matters if you were writing to a file stream or other non-memory stream.)
似乎JsonTextWriter
也不是基类 JsonWriter
实现 IAsyncDisposable
,所以我不得不手动异步关闭 JSON 编写器,而不是通过 using
语句.外部 await using
应确保底层 StreamWriter
在发生异常时不会保持打开状态.
It seems that neither JsonTextWriter
nor the base class JsonWriter
implement IAsyncDisposable
, so I had to asynchronously close the JSON writer manually rather than via a using
statement. The outer await using
should ensure that the underlying StreamWriter
is not left open in the event of an exception.
JSON RFC 8259 指定实现不得在网络传输的 JSON 文本的开头添加字节顺序标记 (U+FEFF).因此,在构造 StreamWriter
时,建议传递一个编码,例如 new UTF8Encoding(false)
不预先添加 BOM.或者,如果您只想要 UTF-8,StreamWriter
构造函数 将创建一个带有 UTF-8 编码的 StreamWriter,没有字节顺序标记 (BOM) 如果您不指定一个自己并为该参数保留一个默认值,如上面的代码所示.
JSON RFC 8259 specifies that Implementations MUST NOT add a byte order mark (U+FEFF) to the beginning of a networked-transmitted JSON text. Thus, when constructing a StreamWriter
, it is recommended to pass an encoding such as new UTF8Encoding(false)
that does not prepend a BOM. Alternatively, if you just want UTF-8, the StreamWriter
constructors will create a StreamWriter with UTF-8 encoding without a Byte-Order Mark (BOM) if you do not specify one yourself and leave a default value for that parameter as is shown in the code above.
这篇关于JToken.WriteToAsync 不写入 JsonWriter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!