繁体   English   中英

多次调用HttpContent ReadAsAsync

[英]Multiple Calls to HttpContent ReadAsAsync

使用Web API 2.2,假设我想从HttpContent读取两次,每次都作为不同的类型。

await httpContent.LoadIntoBufferAsync(); //necessary to buffer content for multiple reads
var X = await httpContent.ReadAsAsync<T>(); //read as first type
var Y = await httpContent.ReadAsAsync<Dictionary<string, object>>(); //read as second type

当我运行上面的代码时, XT的非null实例,而Y是null。 如果我切换顺序, Y将是非空字典,而X将为空。 换句话说,对ReadAsAsync的第二次和后续调用将始终返回null,除非使用相同的泛型类型参数调用它们。 独立地,对ReadAsAsync调用按预期工作(即使在不必要地调用LoadIntoBufferAsync )。

这对我来说是意料之外的 - 似乎我应该能够根据需要多次读取缓冲内容作为不同的类型。 如果我添加另一行:

var Z = await httpContent.ReadAsString();

结果是Z将是非空字符串,无论分配给X, Y, Z的顺序如何。

那么为什么会发生这种情况,为什么我不能使用多种类型的ReadAsAsyncHttpContent读取?

@Peter是对的。 如果您想一次又一次地阅读,您可能希望读取为流并在每次读取流时寻求开始。 但是如果你想现在做什么但是让第二次读取工作,你可以在第一次读取之后寻找流的开头,就像这样。

await httpContent.LoadIntoBufferAsync();
var X = await httpContent.ReadAsAsync<T>();

Stream stream = await httpContent.ReadAsStreamAsync();
stream.Seek(0, SeekOrigin.Begin);

var Y = await httpContent.ReadAsAsync<Dictionary<string, object>>();

文档在这个问题上很少,但对我而言, HttpContent就像流一样,并不是太令人惊讶,因为你只能阅读一次。 几乎所有.NET中使用“read”的方法都是这样的。

我不知道为什么甚至有意义地多次读取相同的数据,每次都以不同的方式解释它,除了可能用于调试目的。 你的例子似乎是为我设计的。 但是如果你真的想这样做,你可以尝试ReadAsStreamAsync() ,然后你可以直接从Stream读取,每次想要再次读取时将Position属性重置为0,或者ReadAsByteArrayAsync() ,给你一个字节您可以根据自己的喜好阅读数组。

当然,您必须明确地使用格式化程序转换为所需的类型。 但这不应该是一个太大的障碍。

我得到了一个有效的解决方案,但它需要使用ReadAsync的重载,它明确地获取媒体格式化程序列表。 它看起来几乎是一个讨厌的黑客,但它的工作原理。

实际上, HttpContent充当引擎盖下的流,一旦格式化程序读取它,它就不会自动重新启动。 但是有一种方法可以进行手动倒带,这就是如何做到的。

首先,为媒体类型格式化程序创建一个装饰器,如下所示:

public class RewindStreamFormatterDecorator : MediaTypeFormatter
{
    private readonly MediaTypeFormatter formatter;

    public RewindStreamFormatterDecorator(MediaTypeFormatter formatter)
    {
        this.formatter = formatter;

        this.SupportedMediaTypes.Clear();
        foreach(var type in formatter.SupportedMediaTypes)
            this.SupportedMediaTypes.Add(type);

        this.SupportedEncodings.Clear();
        foreach(var encoding in formatter.SupportedEncodings)
            this.SupportedEncodings.Add(encoding);
    }

    public override bool CanReadType(Type type)
    {
        return formatter.CanReadType(type);
    }

    public override Task<object> ReadFromStreamAsync(
        Type type,
        Stream readStream,
        HttpContent content,
        IFormatterLogger formatterLogger,
        CancellationToken cancellationToken)
    {
        var result = formatter.ReadFromStreamAsync
           (type, readStream, content, formatterLogger, cancellationToken);
        readStream.Seek(0, SeekOrigin.Begin);
        return result;
    }

    //There are more overridable methods but none seem to be used by ReadAsAsync
}

其次,将格式化程序列表转换为装饰格式化程序列表:

formatters = formatters.Select(f => new RewindStreamFormatterDecorator(f)).ToArray();

...现在您可以根据需要多次调用ReadAsAsync

var X = await httpContent.ReadAsAsync<T>(formatters);
var Y = await httpContent.ReadAsAsync<Dictionary<string, object>>(formatters);

我在自定义模型绑定器中使用了此解决方案,因此我从传递给构造函数的HttpParameterDescriptor实例中HttpParameterDescriptor了formatters集合。 你可能会在执行上下文的某个地方有一个这样的集合,但如果没有,只需像ASP.NET一样创建一个默认集合:

formatters = new MediaTypeFormatter[]
{
    new JsonMediaTypeFormatter(),
    new XmlMediaTypeFormatter(),
    new FormUrlEncodedMediaTypeFormatter()
};

您应该将内容读入字符串,然后将其反序列化为您需要的任何数据类型:

var content = await httpContent.ReadAsString();

// read as first type
var X = JsonConvert.DeserializeObject<T>(content);

// read as second type
var Y = JsonConvert.DeserializeObject<Dictionary<string, object>>(content);

两次异步读取内容没有任何意义。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM