当原始请求有内容时如何克隆 HttpRequestMessage?

Posted

技术标签:

【中文标题】当原始请求有内容时如何克隆 HttpRequestMessage?【英文标题】:How to clone a HttpRequestMessage when the original request has Content? 【发布时间】:2014-07-30 18:23:24 【问题描述】:

我正在尝试使用此答案中概述的方法克隆请求: https://***.com/a/18014515/406322

但是,如果原始请求有内容,我会收到 ObjectDisposedException。

如何可靠地克隆 HttpRequestMessage?

【问题讨论】:

我不知道你能不能。例如,如果您打开一个流并将其用作内容,则该流只能使用一次。原件是在哪里创建的? 我的原始请求是一个PUT,带有一个身份验证头和一些参数作为内容。我需要重试(但每次重试都要更改标题),那么我的选择是什么? 【参考方案1】:

这应该可以解决问题:

    public static async Task<HttpRequestMessage> CloneHttpRequestMessageAsync(HttpRequestMessage req)
    
        HttpRequestMessage clone = new HttpRequestMessage(req.Method, req.RequestUri);

        // Copy the request's content (via a MemoryStream) into the cloned object
        var ms = new MemoryStream();
        if (req.Content != null)
        
            await req.Content.CopyToAsync(ms).ConfigureAwait(false);
            ms.Position = 0;
            clone.Content = new StreamContent(ms);

            // Copy the content headers
            if (req.Content.Headers != null)
                foreach (var h in req.Content.Headers)
                    clone.Content.Headers.Add(h.Key, h.Value);
        


        clone.Version = req.Version;

        foreach (KeyValuePair<string, object> prop in req.Properties)
            clone.Properties.Add(prop);

        foreach (KeyValuePair<string, IEnumerable<string>> header in req.Headers)
            clone.Headers.TryAddWithoutValidation(header.Key, header.Value);

        return clone;
    

【讨论】:

我刚刚使用了这个 sn-p,到目前为止它工作正常。 @Prahbu:你能把它标记为正确答案吗? 使用“using”关键字避免内存泄漏:using var ms = new MemoryStream(); 从 .net 5 开始,req.Content.Headers 永远不会是 null,因此检查是否为空是没有意义的。我只是在没有条件的情况下运行循环。 req.Properties 也已过时,应使用 req.Options 代替。 所以在 .NET 5 中,它应该更改为 foreach (KeyValuePair&lt;string, object?&gt; option in req.Options) clone.Options.Set(new HttpRequestOptionsKey&lt;object?&gt;(option.Key), option.Value); 为什么不只是clone.Content = req.Content【参考方案2】:

如果对内容调用 LoadIntoBufferAsync,可以保证内容缓冲在 HttpContent 对象内部。剩下的唯一问题就是读取流并不会重置位置,所以需要ReadAsStreamAsync,并设置流Position = 0。

我的示例与 Carlos 展示的示例非常相似...

 private async Task<HttpResponseMessage> CloneResponseAsync(HttpResponseMessage response)
        
            var newResponse = new HttpResponseMessage(response.StatusCode);
            var ms = new MemoryStream();

            foreach (var v in response.Headers) newResponse.Headers.TryAddWithoutValidation(v.Key, v.Value);
            if (response.Content != null)
            
                await response.Content.CopyToAsync(ms).ConfigureAwait(false);
                ms.Position = 0;
                newResponse.Content = new StreamContent(ms);

                foreach (var v in response.Content.Headers) newResponse.Content.Headers.TryAddWithoutValidation(v.Key, v.Value);

            
            return newResponse;
        

```

【讨论】:

链接已失效,或许您可以将相关的 sn-p 添加到您的答案中? 文件似乎已被移动,并且 LoadIntoBufferAsync 已被较新的提交删除:github.com/tavis-software/Tavis.HttpCache/commit/… 请注意:此答案与 HttpResponseMessage 克隆有关。问题是关于克隆请求,而不是响应。方法非常相似。【参考方案3】:

Carlos 的一些 linq 快捷方式的回答:

public static async Task<HttpRequestMessage> Clone(this HttpRequestMessage httpRequestMessage)

    HttpRequestMessage httpRequestMessageClone = new HttpRequestMessage(httpRequestMessage.Method, httpRequestMessage.RequestUri);

    if (httpRequestMessage.Content != null)
    
        var ms = new MemoryStream();
        await httpRequestMessage.Content.CopyToAsync(ms);
        ms.Position = 0;
        httpRequestMessageClone.Content = new StreamContent(ms);

        httpRequestMessage.Content.Headers?.ToList().ForEach(header => httpRequestMessageClone.Content.Headers.Add(header.Key, header.Value));
    

    httpRequestMessageClone.Version = httpRequestMessage.Version;

    httpRequestMessage.Properties.ToList().ForEach(props => httpRequestMessageClone.Properties.Add(props));
    httpRequestMessage.Headers.ToList().ForEach(header => httpRequestMessageClone.Headers.TryAddWithoutValidation(header.Key, header.Value));

    return httpRequestMessageClone;

【讨论】:

以上是关于当原始请求有内容时如何克隆 HttpRequestMessage?的主要内容,如果未能解决你的问题,请参考以下文章

从 Microsoft.AspNetCore.Http.HttpRequest 获取原始 URL

在发出克隆请求之前更新令牌

尝试在http拦截器中捕获401,刷新我的会话并重试原始请求

在“停止”事件期间如何定位克隆的可拖动元素?

如何使用 open office uno API 克隆 TextTable 并将 N 个克隆的 TextTable 粘贴到原始 TextTable 下方

本地和原始存储库 git 之间没有区别。但是当其他人克隆项目时,在 H2DB 中找不到该表?