Ladda upp filer till en IIS WebDav-katalog med hjälp av WinINet-API:et
Den här artikeln beskriver hur du programmatiskt laddar upp filer till en WebDav-katalog som finns på Microsoft Internet Information Services (IIS) med hjälp av WinINet-API:et.
Ursprunglig produktversion: Internet Information Services
Ursprungligt KB-nummer: 2001156
Symptom
Programmet laddar programmatiskt upp filer till en WebDav-katalog som finns på IIS, med hjälp av WinINet-API:et för att skicka ett HTTP-verb (Hypertext Transfer Protocol). PUT
Du märker att programmet fungerar korrekt på IIS och Windows Server, men kan misslyckas med att ladda upp filer till IIS WebDav-katalogen, även om inga kodändringar har gjorts i WinINet-programmet.
Orsak
Orsaken till det här problemet beror på en designändring för WebDav som körs på IIS. WebDav som körs på IIS kräver nu autentisering och fungerar inte om endast anonym autentisering används. Därför kommer ditt program som använder WinINet API-sekvensen för HttpSendRequestEx
, InternetWriteFile
HttpEndRequest
att uppleva att anropet till HttpEndRequest
returnerar FALSE och GetLastError()
anger en felkod 12032 – ERROR_INTERNET_FORCE_RETRY
.
Åtgärd
Lösningen på det här problemet är att försöka utföra samma åtgärdssekvens igen, nämligen:
HttpSendRequestEx
InternetWriteFile
HttpEndRequest
Tills HttpEndRequest
returnerar inte FALSE och GetLastError()
rapporterar inte tillbaka 12032 (eller så finns det något annat fel). Om IIS visas med felaktig autentiseringsinformation fortsätter IIS att returnera http-fel 401 för varje nytt försök. Därför måste du hålla reda på hur många gånger HttpEndRequest
funktionen returnerar fel 12032 och förhindra att den körs i en oändlig loop.
Om det finns Windows NTLM-autentisering HttpEndRequest
returneras fel 12032 i högst två gånger för att uppfylla trevägshandskakningen i NTLM. Det första felet 12032 anger ett HTTP-fel 401-svar från servern och det andra felet 12032 anger typ 2 NTLM-handskakningsmeddelandet från servern, vilket, om giltig autentiseringsinformation skickas till IIS, användaren autentiseras korrekt och uppladdningen lyckas.
När du använder en omprövningslogik för att anropa funktionerna ovan i en loop ser du att anropet till InternetWriteFile
görs flera gånger. Det innebär att anropet till slutar skriva data via nätverket, vilket leder till InternetWriteFile
slöseri med bandbredd. För att förhindra detta kan du skicka en HTTP-dummy-begäran HEAD
till servern, som i förväg autentiserar begäran och gör att det senare anropet av HttpSendRequest
inte skickar HTTP-nyttolasten när InternetWriteFile
anropas. Om du är bekant med nätverksövervakaren eller WinINet-loggning ser du att den första PUT
begäran som skickas till servern har en innehållslängd på noll, vilket förhindrar nyttolastöverföringen och nyttolasten överförs inte förrän NTLM-handskakningen är klar.
WebDav-funktionen kräver inte att du använder Windows-autentisering. du kan konfigurera din WebDav-server att använda grundläggande autentisering via SSL, vilket säkerställer säkerheten för datauppladdningen. När grundläggande autentisering har konfigurerats kan du direkt mata in en giltig base-64-kodad lösenordssträng för användarnamn i den utgående begäran, vilket förhindrar att IIS returnerar ett HTTP-fel 401 och det är därför HttpEndRequest
som inte returnerar fel 12032. Du kan lägga till grundläggande autentiseringsinformation i den utgående begäran genom att anropa WinINet-API:et:
HttpAddRequestHeaders(hRequest, "Authorization: Basic <<valid base-64 encoded username:password string>>\r\n", -1, HTTP_ADDREQ_FLAG_ADD);
Gör detta innan du anropar HttpSendRequestEx
för att direkt mata in auktoriseringshuvudet i den utgående HTTP-begäran.
Följande kodexempel visar hur du kan använda logiken för återförsök för att hantera retursvaret 12032 från HttpEndRequest
. Det här exemplet omfattar inte den förautentiserade begäran som beskrivs ovan. För att förautentisera HttpSendRequestEx
behöver du bara anropa HttpOpenRequest
, HttpSendRequest
med HTTP-verbet HEAD
till målservern innan du anropar koden nedan.
Kodexempel
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;
}
}
Mer information
Feedback
https://aka.ms/ContentUserFeedback.
Kommer snart: Under hela 2024 kommer vi att fasa ut GitHub-problem som feedbackmekanism för innehåll och ersätta det med ett nytt feedbacksystem. Mer information finns i:Skicka och visa feedback för