Conversion of VB6 HTTP request to VB.Net 2.0

2019-07-07 20:43发布

问题:

I'm attempting to update a legacy VB6 component (not written by me) to the .NET platform. There is one function which posts an XML string to a URL:

Function PostToUrl(ByRef psUrl, ByRef psData, Byref psResponseText, ByRef psErrorMsg, ByRef psUsername, ByRef psPassword)

    On Error Resume Next    
    Dim objWinHTTP

    PostToUrl = False
    psErrorMsg = ""
    psResponseText = ""
    Dim m_lHTTPREQUEST_SETCREDENTIALS_FOR_SERVER
    m_lHTTPREQUEST_SETCREDENTIALS_FOR_SERVER    =0  

    Set objWinHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")


    objWinHTTP.Open "POST", psUrl

    If psUsername <> "" Then
        Call objWinHTTP.SetCredentials(psUsername, psPassword, m_lHTTPREQUEST_SETCREDENTIALS_FOR_SERVER)
    End If
    objWinHTTP.SetRequestHeader "Host", "http://www.xxx.com/CSIHTTP.asp"
    objWinHTTP.SetRequestHeader "X-Method", "Submit"
    objWinHTTP.SetRequestHeader "Content-Type", "text/xml"

    objwinHTTP.setTimeouts 3000, 15000, 15000, 120000


    Call objWinHTTP.Send(psData)

    ' Handle errors  '


    If Err.Number <> 0 Then

        psErrorMsg = Err.Description & " test"
        PostToUrl = False

    ElseIf objWinHTTP.Status <> 200 AND objWinHTTP.Status <> 202 Then

        psErrorMsg = "(" & objWinHTTP.Status & ") " & objWinHTTP.StatusText
        PostToUrl = False

    Else

        psErrorMsg = objWinHTTP.ResponseText & "(" & objWinHTTP.Status & ") " & objWinHTTP.StatusText
        PostToUrl = True

    End If

    Set objWinHTTP = Nothing
End Function

I've updated this to:

Public Function PostXml(ByVal XML As String) As Boolean

        Try
            Dim URL As String = My.Settings.NTSPostURL 
            'TODO: supply username and password!  '
            Dim Bytes As Byte() = Me.Encoding.GetBytes(XML)
            Dim HTTPRequest As HttpWebRequest = DirectCast(WebRequest.Create(Me.PostURL), HttpWebRequest)
            Dim Cred As New NetworkCredential("username", "password", "http://www.xxx.com")

            With HTTPRequest
                .Method = "POST"
                .ContentLength = Bytes.Length
                .ContentType = "text/xml"
                .Credentials = Cred
                .Timeout = 120000
                .Method = "Submit"
            End With

            Using RequestStream As Stream = HTTPRequest.GetRequestStream()
                RequestStream.Write(Bytes, 0, Bytes.Length)
                RequestStream.Close()
            End Using

            Using Response As HttpWebResponse = DirectCast(HTTPRequest.GetResponse(), HttpWebResponse)

                If Response.StatusCode <> HttpStatusCode.OK Then

                    Dim message As String = [String].Format("POST failed. Received HTTP {0}", Response.StatusCode)

                    Throw New ApplicationException(message)

                End If

            End Using

        Catch ex As WebException
            Dim s As String = ex.Response.ToString() & " " & ex.Status.ToString()
            Throw
        End Try

    End Function

However when I run the .NET code the server returns the error '403 Forbidden - protocol error' on the line: Using Response As HttpWebResponse = DirectCast(HTTPRequest.GetResponse(), HttpWebResponse) . The VB6 code runs fine. Can anyone identify any discrepancies between the two that might be causing this? It's left me stumped....

回答1:

Doesnt look like you are setting a HOST header int he .net version, unless I am missing it. If your server is using HostHeaderResolution to host multiple domains on the same ip address, then you will get a 403.

This is just a wild guess of course.



回答2:

Have you tried capturing the traffic from the two sources and comparing them?

I'd start by trying Fiddler http://www.fiddler2.com/fiddler2/ which is a free HTTP debugging proxy.

If that doesn't work you can try WireShark which is a free protocol analyzer (a bit lower level so there will be a lot of detail to filter out). http://www.wireshark.org/



回答3:

I don't think you've got the Credential thing sorted correctly. You are using the authority portion of the URL as a credential domain.

Try it like this:-

Dim myCache as New CredentialCache()
myCache.Add(New Uri("http://www.xxx.com"), "Basic", New NetworkCredential("username", "password"))

HTTPRequest.Credentials = myCache

This assumes that Basic authentication is needed, another contender would be "Digest" or "NTLM" which may require a domain. For example if the text of the username is like this "domain\username" in the old code then you would want:-

Dim myCache as New CredentialCache()
myCache.Add(New Uri("http://www.xxx.com"), "NTLM", New NetworkCredential("username", "password", "domain"))

HTTPRequest.Credentials = myCache

BTW, I agree with steamer25, for this sort of thing you really need a tool such as fiddler.

If you have an example of the old stuff working place fiddler on the machine where this code runs. Start fiddler and use the command proxycfg -u. Now you can observe the converstation the original stuff has with the ASP server. Use proxycfg -d before closing fiddler.

Now in your .NET code place the following in the app.config:-

<system.net>
    <defaultProxy enabled="false">
        <proxy proxyaddress="http://127.0.0.1:8888" bypassonlocal="False"/>
    </defaultProxy>
</system.net>

Start fiddler and flick the enabled attribute to "true". Now you should see the conversation your new code is trying to have with the ASP Server. Make sure you set enabled="false" before using your new code without fiddler capturing.



回答4:

After much pain I've finally managed to get it working. Problem was with the text encoding, I've changed it to ASCII and it all works:

Try
            Dim Bytes As Byte() = Me.Encoding.GetBytes(XML)
            Dim HTTPRequest As HttpWebRequest = DirectCast(WebRequest.Create(Me.PostURL), HttpWebRequest)

            With HTTPRequest
                .Method = "POST"
                .ContentLength = Bytes.Length
                .ContentType = "text/xml"
                .Credentials = New NetworkCredential("user", "password") 'myCache
            End With

            Using RequestStream As Stream = HTTPRequest.GetRequestStream()
                RequestStream.Write(Bytes, 0, Bytes.Length)
                RequestStream.Close()
            End Using

            Using Response As HttpWebResponse = DirectCast(HTTPRequest.GetResponse(), HttpWebResponse)

                Return Response.StatusCode

            End Using

        Catch ex As WebException
            Dim s As String = ex.Status.ToString
            Throw
        End Try

Thanks to all who helped. Upvotes all round.