Hochladen von Dateien in ein IIS-WebDav-Verzeichnis mithilfe der WinINet-API

In diesem Artikel wird beschrieben, wie Sie Dateien mithilfe der WinINet-API programmgesteuert in ein auf Microsoft-Internetinformationsdienste (IIS) gehostetes WebDav-Verzeichnis hochladen.

Ursprüngliche Produktversion:   Internetinformationsdienste
Ursprüngliche KB-Nummer:   2001156

Problembeschreibung

Ihre Anwendung hochgeladen Dateien programmgesteuert in ein WebDav-Verzeichnis, das auf IIS gehostet wird, mithilfe der WinINet-API, um ein Hypertext Transfer Protocol (HTTP)-Verb zu PUT senden. Sie stellen fest, dass Ihre Anwendung in IIS und Windows Server ordnungsgemäß funktioniert, aber möglicherweise keine Dateien in das IIS-WebDav-Verzeichnis hochzuladen, obwohl keine Codeänderungen an Ihrer WinINet-Anwendung vorgenommen wurden.

Ursache

Die Ursache dieses Problems ist eine Entwurfsänderung für WebDav, die auf IIS ausgeführt wird. WebDav, das auf IIS ausgeführt wird, erfordert jetzt eine Authentifizierung und funktioniert nicht, wenn nur die anonyme Authentifizierung verwendet wird. Aus diesem Grund wird in Ihrer Anwendung, die die WinINet-API-Sequenz von verwendet, der Aufruf zur Rückgabe von FALSE und der Fehlercode HttpSendRequestEx InternetWriteFile HttpEndRequest HttpEndRequest GetLastError() 12032 - ERROR_INTERNET_FORCE_RETRY angezeigt.

Lösung

Die Lösung für dieses Problem besteht in der Wiederholung derselben Reihenfolge von Vorgängen:

  1. HttpSendRequestEx
  2. InternetWriteFile
  3. HttpEndRequest

Bis false nicht zurückgegeben wird und HttpEndRequest 12032 nicht zurückgegeben wird (oder ein anderer GetLastError() Fehler auftritt). Wenn FÜR IIS falsche Authentifizierungsinformationen angezeigt werden, gibt IIS für jeden Wiederholungsversuch immer wieder einen HTTP-Fehler 401 zurück. Sie müssen also nachverfolgen, wie oft die Funktion den Fehler 12032 zurückgibt, und verhindern, dass sie in eine HttpEndRequest Endlosschleife läuft.

Wenn windows NTLM-Authentifizierung ist, wird der Fehler 12032 maximal zweimal zurückgegeben, um den dreistufigen HttpEndRequest NTLM-Handshake zu erfüllen. Der erste Fehler 12032 gibt eine ANTWORT des Httpfehlers 401 vom Server an, und der zweite Fehler 12032 gibt die NtLM-Handshake-Nachricht vom Typ 2 vom Server an. Wenn gültige Authentifizierungsinformationen an IIS übergeben werden, wird der Benutzer ordnungsgemäß authentifiziert, und der Upload ist erfolgreich.

Wenn Sie eine Wiederholungslogik zum Aufrufen der oben genannten Funktionen in einer Schleife verwenden, beachten Sie, dass der Aufruf mehrmals InternetWriteFile erfolgt. Dies bedeutet, dass der Anruf die Daten über das Netzwerk schreibt, was zu InternetWriteFile einer Auslastung der Bandbreite führt. Um dies zu verhindern, können Sie eine Dummy-HTTP-Anforderung an den Server senden, die die Anforderung vorab authentifiziert und dazu führen soll, dass die HTTP-Nutzlast beim Aufruf später nicht gesendet HEAD HttpSendRequest InternetWriteFile wird. Wenn Sie mit der Netzwerkmonitor- oder WinINet-Protokollierung vertraut sind, werden Sie sehen, dass die erste an den Server gesendete Anforderung die Inhaltslänge null hat, wodurch die Nutzlastübertragung verhindert wird und die Nutzlast erst übertragen wird, wenn der PUT NTLM-Handshake abgeschlossen ist.

Für das #A0 ist die Verwendung der #A1 nicht erforderlich. Sie können Ihren WebDav-Server für die Verwendung der Standardauthentifizierung über SSL konfigurieren, wodurch die Sicherheit des Datenuploads sichergestellt wird. Wenn die Standardauthentifizierung konfiguriert ist, können Sie direkt eine gültige Base64-codierte Kennwortzeichenfolge für den Benutzernamen in die ausgehende Anforderung einf?en, die verhindert, dass IIS einen HTTP-Fehler 401 zurücksenst, und aus diesem Grund wird der Fehler HttpEndRequest 12032 nicht zurückgegeben. Sie können die Grundlegenden Authentifizierungsinformationen zur ausgehenden Anforderung hinzufügen, indem Sie die WinINet-API aufrufen:

HttpAddRequestHeaders(hRequest, "Authorization: Basic <<valid base-64 encoded username:password string>>\r\n", -1, HTTP_ADDREQ_FLAG_ADD);

Tun Sie dies vor dem HttpSendRequestEx Aufruf, um den Autorisierungsheader direkt in die ausgehende HTTP-Anforderung zu injizieren.

Das folgende Codebeispiel zeigt, wie Sie die Wiederholungslogik verwenden können, um die Fehlerantwort 12032 zu HttpEndRequest behandeln. In diesem Beispiel wird die oben beschriebene Anforderung vor der Authentifizierung nicht behandelt. Vor der Authentifizierung müssen Sie nur das "HTTP"-Verb mit dem Zielserver aufrufen, bevor Sie den folgenden HttpOpenRequest HttpSendRequest Code HEAD HttpSendRequestEx aufrufen.

Codebeispiel

BOOL UseHttpSendReqEx(HINTERNET hRequest, DWORD dwPostSize)
{
    INTERNET_BUFFERS BufferIn;
    DWORD dwBytesWritten;
    int iChunkCtr;
    BYTE pBuffer[1024];
    BOOL bRet;
    BufferIn.dwStructSize = sizeof( INTERNET_BUFFERS ); // Must be set or you will get an error
    BufferIn.Next = NULL;
    BufferIn.lpcszHeader = NULL;
    BufferIn.dwHeadersLength = 0;
    BufferIn.dwHeadersTotal = 0;
    BufferIn.lpvBuffer = NULL;
    BufferIn.dwBufferLength = 0;
    BufferIn.dwBufferTotal = dwPostSize; // This is the only member used other than dwStructSize
    BufferIn.dwOffsetLow = 0;
    BufferIn.dwOffsetHigh = 0;
    //  The following variable will keep track of the number of times HttpSendRequestEx is called
    int iNumTrials = 0;
    bool bRetVal = FALSE;
    //  The retry goto is to re-try the operation when HttpEndRequest returns error 12032.
    while(1)
    {
        if(!HttpSendRequestEx( hRequest, &BufferIn, NULL, 0, 0))
        {
            printf( "Error on HttpSendRequestEx %d\n",GetLastError());
            return FALSE;
        }
        FillMemory(pBuffer, 1024, 'D'); // Fill buffer with data
        bRet=TRUE;
        for(iChunkCtr=1; iChunkCtr<=(int)dwPostSize/1024 && bRet; iChunkCtr++)
        {
            dwBytesWritten = 0;
            if(bRet=InternetWriteFile( hRequest, pBuffer, 1024, &dwBytesWritten))
                printf( "\r%d bytes sent.", iChunkCtr*1024);
        }
        if(!bRet)
        {
            printf( "\nError on InternetWriteFile %lu\n",GetLastError());
            return FALSE;
        }
        if(!HttpEndRequest(hRequest, NULL, 0, 0))
        {
            int iLastError = GetLastError();
            printf( "Error on HttpEndRequest %lu \n", iLastError);
            //  Use the following logic to "retry" after receiving error 12032 from HttpEndRequest
            //
            //  Error 12032 = ERROR_INTERNET_FORCE_RETRY means that you just need to send the request again
            //
            //  Sending request again means that you simply need to call:
            //
            //  HttpSendRequest, InternetWriteFile, HttpEndRequest until HttpEndRequest does not return
            //  back error 12032.
            //
            //  Since NTLM is a 3-way handshake protocol, it will happen that HttpEndRequest will return
            //  error 12032 two times and hence the following check.
            //
            //  If error 12032 is returned 3 or more number of times, then there is some Other error.
            if(iLastError == 12032 && iNumTrials < 3) {
                iNumTrials++;
                continue;   // This will retry HttpSendRequestEx...
            }
            return FALSE;
        }
        return TRUE;
    }
}

Weitere Informationen