是否有 StreamReader 的 Peek 方法的异步等价物?

Is there an async equivalent of StreamReader's Peek method?

我正在更新一些 netstandard2.0 代码以不从 HttpRequest.Body (a Stream) 同步读取(这会在 netcoreapp3.0 中引发异常,除非您设置 AllowSynchronousIO 选项,这显然不是一个好主意)。

我已经转换了 JSON 反序列化部分(使用 System.Text.Json),但在此之前它使用 StreamReader 做了一个偷偷摸摸的 .Peek()(为了看看 body 是一个对象还是一个数组),我不确定在哪里可以找到异步且不会消耗 Stream.

的现代替代方案
using var reader = new StreamReader(stream);
switch (reader.Peek()) // TODO: Find an async equivalent!
{
    case '{':
        return await JsonSerializer.DeserializeAsync<GraphQLRequest>(stream);
    case '[':
        return await JsonSerializer.DeserializeAsync<GraphQLRequest[]>(stream);
    default:
        // ...
}

实际上,Peek 方法首先将流读入其缓冲区,然后为您提供其中的值(请查看 dotnet runtime repo)。所以它移动了流的位置。但是,如果不阅读并寻找流,就无法实现 peek 功能。

由于您无法返回 HttpRequestStream,更好的策略是读取整个内容,然后使用 Utf8JsonReader 确定请求是对象还是数组。请注意,Utf8JsonReader 不能在异步方法中声明,所以我不得不将它放在一个单独的非异步方法中。

private static JsonTokenType GetTokenType(byte[] bytes)
{
    var reader = new Utf8JsonReader(bytes.AsSpan());
    reader.Read();
    return reader.TokenType;
}
var ms = new MemoryStream();
await Request.Body.CopyToAsync(ms);
var jsonBytes = ms.ToArray();

switch (GetTokenType(jsonBytes)) 
{
    case JsonTokenType.StartObject:
        return JsonSerializer.Deserialize<GraphQLRequest>(jsonBytes);
    case JsonTokenType.StartArray:
        return JsonSerializer.Deserialize<GraphQLRequest[]>(jsonBytes);
    default:
        // ...
}

由于我们的独特情况,在反复讨论这个问题之后,我们(幸运的是!)从@davidfowl 那里得到了一些帮助,他使用 PipeReader 为我们创建了一个示例,它是 AdvanceTo 方法来查看令牌但不使用流,因此我们可以直接从流中使用 Deserialize

完整来源here