JToken.WriteToAsync 不写入 JsonWriter [英] JToken.WriteToAsync does not write to JsonWriter

查看:16
本文介绍了JToken.WriteToAsync 不写入 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; }
}

推荐答案

您需要正确刷新和关闭 JsonTextWriterStreamWriter 以完全填充 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 the StreamWriter. This syntax guarantees that the StreamWriter will be flushed and closed asynchronously, and can be used on any object that implements IAsyncDisposable. (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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆