在异常期间捕获原始 HTTP POST 数据

Capturing raw HTTP POST Data during Exception

提问人: 提问时间:5/25/2009 更新时间:1/3/2013 访问量:5168

问:

我有一个托管在 IIS/ASP.NET 中的 WCF 服务,它接受序列化对象的 HTTP Post(而不是格式发布)。

如果客户端发送格式错误的请求(例如,它们没有正确序列化对象),我想记录发送的消息。

我们已经在使用 ELMAH 来捕获未经处理的异常,因此简单地附加帖子数据是最简单的选择。

我可以在异常期间获取当前的 HttpContext,但这仅包含 HTTP 标头信息。

我的问题是:有没有办法捕获原始HTTP POST请求正文?或者,如果做不到这一点 - 捕获导致错误的输入的更好方法(没有反向代理)?

编辑:澄清一下,始终运行数据包级捕获并不合适。我正在寻找一个可以部署到生产服务器的解决方案,并且该解决方案将拥有我们无法控制或无法监控的客户端。

编辑 #2:建议访问 Request.InputStream - 如果您在 WCF 从流中读取请求后尝试读取,则此建议不起作用。

这里有一个示例代码,看看我是如何尝试使用它的。

        StringBuilder log = new StringBuilder();

        var request = HttpContext.Current.Request;

        if (request.InputStream != null)
        {
            log.AppendLine(string.Format("request.InputStream.Position = \"{0}\"", request.InputStream.Position));
            if (request.InputStream.Position != 0)
            {
                request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
            }

            using (StreamReader sr = new StreamReader(request.InputStream))
            {
                log.AppendLine(string.Format("Original Input: \"{0}\"", sr.ReadToEnd()));
            }
        }
        else
        {
            log.AppendLine("request.Inputstream = null");
        }


        log.ToString();

日志的输出。ToString() 为:

    request.InputStream.Position = "0"
    Original Input: ""
C# asp.net WCF Elmah 错误报告

评论

1赞 Jeff 5/26/2009
与 StreamReader 一起“使用”将导致 System.Web.Request.InputStream 被释放(这意味着数据丢失)。

答:

-2赞 Larry K 5/25/2009 #1

使用 fiddler。从MS中免费。 效果很好。

评论

1赞 5/25/2009
我说的是当没有 Fiddler、Wireshark 或任何运行来捕获它时发生的错误。例如,在生产或暂存服务器上,无法一直运行数据包捕获。
-1赞 Jeff 5/25/2009 #2

您是否查看了 System.Web.Request.InputStream 属性?它应该有你想要的。

如何“倒带”InputStream 属性。

    if (Request.InputStream.Position != 0)
    {
        Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
    }

您应该研究的另一个选项是在 BeginRequest 事件上使用 HTTPModule 捕获此信息。数据应该在 BeginRequest 事件中存在,因为我不相信 WCF 会在 PostAuthenticateEvent 之后选取请求。

评论

1赞 5/25/2009
我确实查看了该属性 - 不幸的是,该流仅包含未读数据。在发生异常时,WCF 已读取输入数据。您无法倒带/重置位置。
0赞 Jeff 5/25/2009
查看我的更新...由于 Request.InputStream.CanSeek 为 true,因此可以使用 Seek 方法将位置重置回 0。
0赞 5/26/2009
感谢您的更新 - CanSeek 是真的,但是流中仍然没有任何内容可供读取。请参阅更新的文本,例如我为尝试和测试它所做的工作。
3赞 blowdart 5/26/2009 #3

当它到达您的服务时,请求已处理,您无法使用。

然而。。。您可以附加消息检查器。消息检查器允许您在消息到达操作实现之前对其进行处理。您可以创建消息的缓冲副本,并将其复制到 OperationContext.Current 中。

当然,这是丑陋的黑客攻击,这将意味着内存开销,因为现在每个请求都有两个消息副本。

评论

0赞 5/26/2009
谢谢 - 这不是一个特别好的解决方案,但牺牲一些内存可能是值得的。
-1赞 Lucas 1/3/2013 #4

从 ASP.NET 下(IIS 下的 ASP Web 服务),以下代码会有所帮助:

if (request.InputStream.Position != 0)
{
  request.InputStream.Seek(0, System.IO.SeekOrigin.Begin);
}

WCF 可能不同(即它在读取 InputStream 后将其释放)

评论

0赞 1/4/2013
这与杰夫给出的答案相同。它也不起作用,因为流是不可寻回的。