我可以修改Request.Headers集合吗?

时间:2013-03-05 21:49:09

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

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

基本上这是一个奇怪的场景,我只想尝试解决方法。我不知道是谁/什么客户端没有指定用户代理,这似乎是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以确定如何设置该属性以便我可以更新它,但我还没有提出任何东西(不仅有一个私有字段驱动我可以找到的属性)。

3 个答案:

答案 0 :(得分:3)

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

(假设您已经使用自定义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中的用户代理值,但它应该能够解决您需要的问题(以及我的问题)

希望这有帮助:)

答案 1 :(得分:3)

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;
        }
    }
}

答案 2 :(得分:2)

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

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

更新

不幸的是Request.UserAgent不使用Request.Headers中包含的信息,而是调用HttpWorkerRequest中的方法GetKnownRequestHeader。这是一个抽象类,通过查看反编译的库代码,实际实现会根据托管环境而有所不同。因此,我无法通过反射以可靠的方式看到替换用户代理字符串的方法。你可以滚动你自己的WorkerRequest类,但是为了努力,我认为收益不值得。

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

您还可以调查先前注入的内容,例如在IIS或您的代理服务器上,如果您使用它。

另外,我建议将此问题报告给SAP。我知道他们现在正在积极研究Viewer,谁知道他们可能会修复它,甚至可能会增加对Opera的支持!