Can I modify the Request.Headers collection?

2019-04-09 04:48发布

问题:

I have an ASP.NET site that uses a third-party reporting component. This component is misbehaving by throwing a NullReferenceException whenever the client browser is not specifying a User-Agent in the request headers.

It's basically an odd scenario that I'm just trying to come up with a workaround for. I do not know who/what client is not specifying a User-Agent, which seems like bad form IMO, but we have to deal with the exceptions it is generating. I have logged a support ticket with the third-party regarding the bug in their reporting component, but I have my doubts about how fruitful that route is going to be. So my thought was just to detect when the User-Agent is blank and default it to something just to appease the reporting component. However, I can't seem to change anything in the Request.Headers collection. I get the following exception:

Operation is not supported on this platform.

I'm starting to believe I'm not going to be able to do this. I understand why ASP.NET wouldn't allow this, but I haven't come up with any other workaround.

Update: At penfold's suggestion, I tried to add the User-Agent to the Request.Headers collection using an HttpModule. This got it added to the Headers collection, but did nothing to update the Request.UserAgent property, which is what is causing the reporting component to fail. I've been looking through .NET Reflector to determine how that property is set so that I can update it, but I haven't come up with anything yet (there isn't just a private field that drives the property that I can find).

回答1:

I think the best way of handling this is to use a http module that will check the header and inject the user agent if necessary.

As you have found out you cannot use the set method on the Headers object. Instead you will have to inject the user agent string into the header via protected properties that can be accessed through reflection as outlined in the answer to this question.

UPDATE

Unfortunately Request.UserAgent doesn't use the information held in Request.Headers, instead it calls the method GetKnownRequestHeader in HttpWorkerRequest. This is an abstract class and from looking at the decompiled library code the actual implementation varies depending on the hosting environment. Therefore I cannot see a way to replace the user agent string in a reliable manner via reflection. You could roll your own WorkerRequest class but for the amount of effort I don't think the payoff would be worth it.

Sorry to be negative but I think its just not possible to set the user agent within the web application in a simple manner. Your best option at the moment would be to perform a pre-check for a user agent, and if the request doesn't have one return a browser not supported error message.

You could also investigate injecting something earlier on, i.e. in IIS or at your proxy server if you use one.

Also I would recommend that this issue is reported to SAP. I know they are actively working on the Viewer at the moment and who knows they might fix it and maybe even add support for Opera!



回答2:

Recently I also facing similar problem same as you. I overcome the problem of Request.UserAgent by using a mock HttpWorkerRequest.

(Assuming you already solve the agent string in Request.Headers with custom HttpModule)

Here is the sample code:

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

then, in your custom HttpApplication.BeginRequest handler, do this

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

Note: this method still does not replace the user agent value in ServerVariables, but it should able to solve what you need(and my problem too)

Hope this help :)



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