给定一个HttpResponseMessage,我该如何读取请求的内容?

4

给定一个 System.Net.Http.HttpResponseMessage,我可以通过 response.RequestMessage 获取关于请求的信息,例如:

response.RequestMessage.RequestUri // the url of the request
response.RequestMessage.Method     // the HTTP method

然而,我无法找到一种方法来从中得到有用的内容。

response.RequestMessage.Content    // a StringContent instance

我已经查看了StringContent的属性树,但我无法弄清楚如何以在Watch窗口中有效的方式将其内容作为常规字符串获取。

有什么建议吗?


2
你是在对象被释放后观察它吗?那我会期望这种情况发生。你不能观察一个已经被释放的对象,尽管也许有一些技巧可以复制该值。但表达式本身无法再次评估。你正在使用VS2015吗?我记得读到过监视表达式现在得到更好的支持。 - Jeroen Vannevel
@JeroenVannevel:我正在使用VS2013,并在响应返回后进行监视,所以很可能请求对象的内容已经被处理掉了... 有没有办法从HttpResponseMessage获取请求体的内容? - Tomas Aschan
@JeroenVannevel,“灰色”不算:它意味着自上次评估以来该值已过时,仅为您的方便而保留。 - ivan_pozdeev
@ivan_pozdeev:听起来这正是Tomas所需要的。 - Jeroen Vannevel
@JeroenVannevel:你的方法有两个问题:1)它要求我在每个请求之前停止,这意味着我需要比我想要的更频繁地停止,而且没有理由。2)它依赖于响应按顺序返回,并在进行任何其他请求之前返回;否则,在监视窗口中将会有过时的数据,并且无法匹配发送失败响应的请求中发送的内容。 - Tomas Aschan
显示剩余3条评论
1个回答

8
分析使用System.Net.Http.dll v2.2.29.0ILSpy,发现System.Net.Http.HttpClientHandler.CreateResponseMessage(初始化HttpResponse对象的方法)并未对相应的HttpRequest.Content进行任何操作。这意味着,如果一开始它不是null,那么内容对象本身仍然可用。

System.ObjectDisposedException会在"对已释放对象执行操作时"抛出。什么是"已释放对象"?System.Net.Http.StringContent实际上是通过其祖先System.Net.Http.HttpContent实现了System.IDisposable接口。因此,它实际上意味着"在调用.Dispose()方法后的IDisposable对象"

自然地,搜索HttpContent.Dispose()的用户会将我们带到罪魁祸首-System.Net.Http.HttpClient.SendAsync(),该方法在发送数据后调用System.Net.Http.HttpClient.DisposeRequestContent()


现在,关于该怎么做。所有HttpContent.Dispose()所做的就是关闭对象的流并设置标志。然而,StreamContent(或者它的父类ByteArrayContent)将数据保留在.content字段中 - 这个字段不会被处理!可惜的是,直接读取它的两种方法都是protected,所有使用这些方法的公共方法都首先检查标志。因此,读取它的唯一方法是通过反射(示例是用IronPython编写的,给出了C#等价物的注释):
>>> sc=System.Net.Http.StringContent("abcdefghijklmnopqrstuvwxyz")
#in C#, the following is `type(StringContent)' (this is what it actually does)
>>> scd=System.Reflection.TypeDelegator(System.Net.Http.StringContent)
>>> print scd.GetField("content",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)
None     #because the field is in ByteArrayContent and is private
>>> bacd=System.Reflection.TypeDelegator(System.Net.Http.ByteArrayContent)
>>> bacd.GetField("content",System.Reflection.BindingFlags.NonPublic|System.Reflection.BindingFlags.Instance)
<System.Reflection.RtFieldInfo object at 0x000000000000002C [Byte[] content]>
# `_' is last result
>>> _.GetValue(sc)
Array[Byte]((<System.Byte object at 0x000000000000002D [97]>, <System.Byte objec
t at 0x000000000000002E [98]>, <System.Byte object at 0x000000000000002F [99]>,
<...>

在C#中,它看起来像这样:
type(ByteArrayContent)
    .GetField("content",BindingFlags.NonPublic|BindingFlags.Instance)
    .GetValue(content)

好的。反射对于我的使用情况(调试...)绝对是过度设计,所以我会放弃这个想法。不过还是感谢你进行了彻底的挖掘和解释! - Tomas Aschan
你可以在调试器中同样读取字段。如果你不知道,它使用反射来探索对象;-) - ivan_pozdeev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接