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

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

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

Problembeschreibung

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

Ursache

Die Ursache dieses Problems liegt in einer Entwurfsänderung für WebDav, die auf IIS ausgeführt wird. WebDav, der 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 HttpSendRequestEx InternetWriteFile verwendet, HttpEndRequest der Aufruf von FALSE angezeigt und der HttpEndRequest GetLastError() Fehlercode 12032 - ERROR_INTERNET_FORCE_RETRY angezeigt.

Lösung

Die Lösung dieses Problems besteht darin, die gleiche Abfolge von Vorgängen zu wiederholen, nämlich:

  1. HttpSendRequestEx
  2. InternetWriteFile
  3. HttpEndRequest

Until HttpEndRequest gibt nicht FALSE zurück und GetLastError() meldet 12032 nicht zurück (oder es gibt einen anderen Fehler). Wenn IIS falsche Authentifizierungsinformationen angezeigt wird, gibt IIS für jeden Wiederholungsversuch weiterhin den HTTP-Fehler 401 zurück. Sie müssen also nachverfolgen, wie oft die HttpEndRequest Funktion den Fehler 12032 zurückgibt, und verhindern, dass eine Endlosschleife ausgeführt wird.

Wenn Windows NTLM-Authentifizierung vorhanden ist, HttpEndRequest wird der Fehler 12032 maximal zwei Mal zurückgegeben, um den dreiseitigen NTLM-Handshake zu erfüllen. Der erste Fehler 12032 zeigt eine HTTP-Fehler-401-Antwort vom Server an, und der zweite Fehler 12032 gibt die NTLM-Handshake-Nachricht vom Typ 2 vom Server an, die, wenn gültige Authentifizierungsinformationen an IIS übergeben werden, der Benutzer korrekt authentifiziert wird und der Upload erfolgreich ist.

Wenn Sie eine Wiederholungslogik verwenden, um die oben genannten Funktionen in einer Schleife aufzurufen, beachten Sie, dass der Aufruf InternetWriteFile mehrmals ausgeführt wird. Dies bedeutet, dass der Anruf die InternetWriteFile Daten über das Netzwerk schreibt, was zu einer Überschreitung der Bandbreite führt. Um dies zu verhindern, können Sie eine HEAD Pseudo-HTTP-Anforderung an den Server senden, die die Anforderung vorab authentifiziert und bewirkt, dass der spätere Aufruf beim Aufruf nicht die HttpSendRequest HTTP-Nutzlast InternetWriteFile sendet. Wenn Sie mit dem Netzwerkmonitor oder der WinINet-Protokollierung vertraut sind, werden Sie sehen, dass die erste PUT an den Server gesendete Anforderung eine Inhaltslänge von Null aufweist, wodurch die Nutzlastübertragung verhindert wird und die Nutzlast erst übertragen wird, wenn der NTLM-Handshake abgeschlossen ist.

Für das WebDav-Feature müssen Sie nicht Windows Authentifizierung verwenden. Sie können Ihren WebDav-Server so konfigurieren, dass er die Standardauthentifizierung über SSL verwendet, wodurch die Sicherheit des Datenuploads sichergestellt wird. Wenn die Standardauthentifizierung konfiguriert ist, können Sie in der ausgehenden Anforderung direkt eine gültige base64-codierte Benutzernamenkennwortzeichenfolge einfügen, wodurch verhindert wird, dass IIS einen HTTP-Fehler 401 zurückgibt. Deshalb HttpEndRequest wird der Fehler 12032 nicht zurückgegeben. Sie können die Informationen zur Standardauthentifizierung der 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);

Führen Sie dies aus, bevor Sie HttpSendRequestEx aufrufen, um den Autorisierungsheader direkt in die ausgehende HTTP-Anforderung einzufügen.

Das folgende Codebeispiel zeigt, wie Sie die Wiederholungslogik verwenden können, um den Fehler 12032-Rückgabeantwort von HttpEndRequest zu behandeln. In diesem Beispiel wird die oben beschriebene Vorabauthentifizierungsanforderung nicht behandelt. Um sich vorab zu authentifizieren, müssen Sie lediglich HttpOpenRequest HttpSendRequest das HTTP-Verb an den Zielserver aufrufen, bevor Sie den folgenden HEAD Code 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