POST数据后HttpClient.PostAsync HttpContent参数值为null

时间:2014-03-04 16:27:00

标签: c# http-post dotnet-httpclient

我有一个循环遍历URIs的简短列表,需要向每个人发送 相同的 内容。 HttpContent驻留在传递给PostAsync的调用中的变量中。我遇到的问题是 1st 调用完美无缺,但所有后续调用都失败了,因为第一次调用完成后请求内容为null

我认为我对PostAsync电话的理解可能缺乏关于对象的一些低级细节,而且它的生命周期(或缺乏),所以希望有人可以为我阐明它。

代码如下:

HttpResponseMessage httpResponseMessage = httpClient.PostAsync(destionationUri, messageContent).Result;

有问题的变量是messageContent,其类型为MultipartFormDataContent,派生自HttpContent。我想一些将值复制到一个新的独立位置以便重用的粗略方法可能会起作用,但我希望了解为什么会发生这种情况以及允许内容在循环期间持续存在的正确方法。

有谁知道如何解决这个问题,在循环上面的代码时,messageContent值会在迭代之间持续存在吗?

编辑:在每次调用MultipartFormDataContent之前,我使用相同的数据从头开始重构重建 PostAsync代码使用HttpClient实例。重建HtppContent的调用实际上有效;该值不为null。但是,当我现在打电话给PostAsync并访问.Result时,我收到以下错误:

  

将内容复制到流时出错。无法访问已关闭   流。

编辑2:在检查第二次调用我的方法从头开始重建MultipartFormDataContent对象时,我注意到我检查它时的附件属性{ {1}}属性(ContentStream对象的)声明如下:

  

attachment.ContentStream.Position引发了类型异常   Mail.MailMessage

所有属性都表明System.ObjectDisposedException无法再被阅读,因为它被处理掉了。对ContentStream的调用显然是处理流,可能是根本问题。但是我仍然无法找到可行的解决方案。

2 个答案:

答案 0 :(得分:8)

通过创建一个允许重复使用内容类的新“包装器”HttpContent类,可以使解决方案更加整洁。发送内容时,HttpClient会自动处理任何请求内容,“以防万一”。这不是你的理由。幸运的是,Dispose(bool)方法是虚方法,因此您可以覆盖派生类中的dispose行为。

 public class ReusableContent : HttpContent
    {
        private readonly HttpContent _innerContent;

        public ReusableContent(HttpContent innerContent)
        {
            _innerContent = innerContent;
        }

        protected override async Task SerializeToStreamAsync(Stream stream, TransportContext context)
        {
            await _innerContent.CopyToAsync(stream);
        }

        protected override bool TryComputeLength(out long length)
        {
            length = -1;
            return false;
        }

        protected override void Dispose(bool disposing)
        {
            // Don't call base dispose
            //base.Dispose(disposing);
        }
    }

一旦你有了这门课,就可以像这样使用它,

    [Fact]
    public async Task Sending_same_content_multiple_times()
    {
        var client = new HttpClient { BaseAddress = _BaseAddress };


        var stream = new MemoryStream();
        var sw = new StreamWriter(stream);
        sw.Write("This is a stream");
        sw.Flush();
        stream.Position = 0;

        var multipart = new MultipartContent
        {
            new StringContent("This string will be sent repeatedly"),
            new FormUrlEncodedContent(new[] {new KeyValuePair<string, string>("foo", "bar"),}),
            new StreamContent(stream)
        };

        var content = new ReusableContent(multipart);

        for (int i = 0; i < 10; i++)
        {
            var response = await client.PostAsync("devnull", content);
            response.EnsureSuccessStatusCode();
        }

    }

如果您使用此功能,请确保不使用实际需要处理的内容而不手动处理。

答案 1 :(得分:1)

我找到了一个解决方案并且正常工作但实际上并不是我想要找到的答案。正如我在ContentStream阅读时提到的PostAsync().Result()已自动关闭/处理。不是我想要的行为。即使尝试再次使用附件重建MultipartFormDataContent也没有成功。

这篇帖子:https://stackoverflow.com/a/8100653/410937让我想到进一步链接到最初加载到MemoryStream的附件并重新运行该方法。结果是一个有效的解决方案。我基本上必须从头开始重建整个请求对象 ,包括所有原生元素和原始元素,再次然后 POST

对我而言似乎很多,因为我只是想使用相同的内容循环和POST到1..n URIs。所有的细微差别让我一路走回去,从头开始重建请求内容,使其发挥作用。

如果存在的解决方案我真正寻求更多以下内容:

  1. 阅读
  2. 后,通知PostAsync().Result() 关闭我的信息流
  3. 将文件附件流恢复为可再次读取,而无需重新创建整个请求。虽然它被处理掉了,但这可能是不可行的。
  4. 我愿意接受更直接的解决方案,但这确实有效。