JToken.WriteToAsync 不写入 JsonWriter

JToken.WriteToAsync does not write to JsonWriter

我正在尝试创建一个以某种方式更改请求的中间件。我能够阅读它并更改内容,但我无法弄清楚如何正确设置流编写器以创建新主体。当我调用 normalized.WriteToAsync(jsonWriter) 时,MemoryStream 仍然为空,因此我收到 A non-empty request body is required. 异常。我在这里错过了什么?这是我目前所拥有的:

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 等的演示:

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

您需要正确刷新并关闭 JsonTextWriterStreamWriter 以完全填充 memoryStream,如下所示:

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

演示 fiddle #1 here.

或者,由于您正在写入 MemoryStream,所以根本没有必要使用 async,您可以这样做:

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

演示 fiddle #2 here.

备注:

  • 注意 for the StreamWriter. This syntax guarantees that the StreamWriter will be flushed and closed asynchronously, and can be used on any object that implements IAsyncDisposable的用法。 (这仅在您写入文件流或其他非内存流时才真正重要。)

  • 似乎 JsonTextWriter nor the base class JsonWriter 都没有实现 IAsyncDisposable,所以我不得不手动异步关闭 JSON writer,而不是通过 using 语句.外部 await using 应确保底层 StreamWriter 在发生异常时不会保持打开状态。

  • 如果你不要自己指定一个,并为该参数保留一个默认值,如上面的代码所示。