【问题标题】:Can I modify the Request.Headers collection?我可以修改 Request.Headers 集合吗?
【发布时间】:2013-02-20 12:07:00
【问题描述】:

我有一个使用第三方报告组件的 ASP.NET 站点。每当客户端浏览器未在请求标头中指定 User-Agent 时,此组件就会抛出 NullReferenceException,从而导致该组件行为不端。

这基本上是一个奇怪的场景,我只是想想出一个解决方法。我不知道谁/哪个客户端没有指定用户代理,这似乎是 IMO 的错误形式,但我们必须处理它产生的异常。我已经向第三方记录了一份关于他们报告组件中的错误的支持票,但我对这条路线的成果有多大表示怀疑。所以我的想法只是检测User-Agent 何时为空白并将其默认为只是为了安抚报告组件。但是,我似乎无法更改 Request.Headers 集合中的任何内容。我得到以下异常:

Operation is not supported on this platform.

我开始相信我无法做到这一点。我了解为什么 ASP.NET 不允许这样做,但我还没有想出任何其他解决方法。

更新:在 penfold 的建议下,我尝试将 User-Agent 添加到 Request.Headers 集合 using an HttpModule。这将其添加到 Headers 集合中,但没有更新 Request.UserAgent 属性,这是导致报告组件失败的原因。我一直在查看 .NET Reflector 以确定如何设置该属性以便我可以更新它,但我还没有想出任何东西(不只是一个私有字段驱动我可以找到的属性)。

【问题讨论】:

  • 您能否提供您尝试更改 HttpWebRequest 标头的代码?
  • 不,我没有使用 HttpWebRequest——我正在尝试更改传入请求的标头(HttpRequest 对象)。例如:Request.Headers.Set("User-Agent", "some user agent")

标签: asp.net crystal-reports http-headers httprequest user-agent


【解决方案1】:
protected void Application_BeginRequest(Object sender, EventArgs e) {
    const string ua = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)";
    Request.Headers["User-Agent"] = ua;
    var httpWorkerRequestField = Request.GetType().GetField("_wr", BindingFlags.Instance | BindingFlags.NonPublic);
    if (httpWorkerRequestField != null) {
        var httpWorkerRequest = httpWorkerRequestField.GetValue(Request);
        var knownRequestHeadersField = httpWorkerRequest.GetType().GetField("_knownRequestHeaders", BindingFlags.Instance | BindingFlags.NonPublic);
        if (knownRequestHeadersField != null) {
            string[] knownRequestHeaders = (string[])knownRequestHeadersField.GetValue(httpWorkerRequest);
            knownRequestHeaders[39] = ua;
        }
    }
}

【讨论】:

  • 为什么要修改 User-Agent ?
【解决方案2】:

最近我也遇到了和你一样的问题。我克服了问题 Request.UserAgent 使用模拟 HttpWorkerRequest。

(假设您已经使用自定义 HttpModule 解决了 Request.Headers 中的代理字符串)

这里是示例代码:

Friend Class MockedRequestWorker
    Inherits HttpWorkerRequest

    Private ReadOnly _BaseHttpWorkerRequest As HttpWorkerRequest
    Private ReadOnly _UserAgent As String

    Friend Sub New(ByVal base As HttpWorkerRequest, 
        ByVal UserAgent As String)
        _BaseHttpWorkerRequest = base
        _UserAgent = UserAgent
    End Sub

    Public Overrides Sub EndOfRequest()
        _BaseHttpWorkerRequest.EndOfRequest()
    End Sub

    Public Overrides Sub FlushResponse(ByVal finalFlush As Boolean)
        _BaseHttpWorkerRequest.FlushResponse(finalFlush)
    End Sub

    'Note: remember to override all other virtual functions by direct invoke functions
    'from _BaseHttpWorkerRequest, except the following function

    Public Overrides Function GetKnownRequestHeader(ByVal index As Integer) As String

        'if user is requesting the user agent value, we return the 
        'override user agent string
        If index = HttpWorkerRequest.HeaderUserAgent Then
            Return _UserAgent
        End If

        Return _BaseHttpWorkerRequest.GetKnownRequestHeader(index)
    End Function

End Class

然后,在您的自定义 HttpApplication.BeginRequest 处理程序中,执行此操作

Private Sub BeginRequestHandler(ByVal sender As Object, ByVal e As EventArgs)

    Dim request As HttpRequest = HttpRequest.Current.Request
    Dim HttpRequest_wrField As FieldInfo = GetType(HttpRequest).GetField("_wr", BindingFlags.Instance Or BindingFlags.NonPublic)

    Dim ua As String = "your agent string here"
    Dim _wr As HttpWorkerRequest = HttpRequest_wrField.GetValue(request)
    Dim mock As New MockedRequestWorker(_wr, ua)

    'Replace the internal field with our mocked instance
    HttpRequest_wrField.SetValue(request, mock)

End Sub

注意:此方法仍然不能替换 ServerVariables 中的用户代理值,但它应该能够解决您的需要(也是我的问题)

希望得到帮助:)

【讨论】:

  • 在我的特殊情况下,这最终不值得付出努力,但令人印象深刻的工作。感谢分享!
【解决方案3】:

我认为处理此问题的最佳方法是使用 http 模块,该模块将检查标头并在必要时注入用户代理。

正如您所发现的,您不能在 Headers 对象上使用 set 方法。相反,您必须通过 this question 的答案中概述的可通过反射访问的受保护属性将用户代理字符串注入标头。

更新

不幸的是Request.UserAgent 没有使用Request.Headers 中保存的信息,而是调用HttpWorkerRequest 中的方法GetKnownRequestHeader。这是一个抽象类,通过查看反编译的库代码,实际实现会因托管环境而异。因此,我看不到通过反射以可靠方式替换用户代理字符串的方法。您可以推出自己的 WorkerRequest 课程,但我认为付出的努力并不值得。

抱歉,我认为这是不可能的,但我认为不可能以简单的方式在 Web 应用程序中设置用户代理。目前最好的选择是对用户代理执行预检查,如果请求没有,则返回浏览器不支持的错误消息。

您还可以调查早期注入的东西,即在 IIS 中或在您的代理服务器上(如果您使用的话)。

我还建议将此问题报告给 SAP。我知道他们目前正在积极开发 Viewer,谁知道他们可能会修复它,甚至可能会添加对 Opera 的支持!

【讨论】:

  • 谢谢。这似乎将值放入 Headers 集合中,但 Request.UserAgent 属性并未反映新值(仍然为空/无),Crystal 仍在爆炸。会赞成有用的响应,但知道如何强制从 Headers 集合重新加载基于标题的属性?
  • 实际上,我敢打赌我的问题是我没有实现为 HTTP 模块。我刚刚将该代码构建到我的页面中进行测试。嗯...我必须看看它是否可以作为 HTTP 模块工作,因为我打赌这将在生命周期的早期发生。如果我能让它以这种方式工作,我会接受。再次感谢!
  • Crystal Web 插件使用 http 处理程序来处理报告生成,因此将注入代码放在 aspx 页面上远远晚于生命周期。出于您的目的,修改请求标头的唯一方法是创建一个拦截 BeginRequest 并在此时注入用户代理的模块。
  • 我在 BeginRequest 事件中将其实现为 HttpModule ......但仍然没有骰子。正在将 User-Agent 添加到 Headers 集合中,但 Request.UserAgent 仍然为空,Crystal 仍然在它上面窒息。在设置请求属性之前是否会执行另一个事件?再次感谢您的帮助...
  • 感谢更新。你一直很有帮助。我最终接受了您的建议并实施了“不支持浏览器”消息以避免异常。 FWIW,我确实通知了 SAP,他们的回应是他们不支持未指定用户代理的浏览器,并且“这不是我们要解决的问题。”!!去图...
猜你喜欢
  • 2020-01-02
  • 1970-01-01
  • 2019-01-30
  • 2011-11-13
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 2016-11-15
相关资源
最近更新 更多