是否有 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。
我正在更新一些 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。