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:
HttpSendRequestEx
InternetWriteFile
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;
}
}