Se produce un error en una solicitud POST o PUT cuando se usa la clase HttpWebRequest para enviar una gran cantidad de datos

Este artículo le ayuda a resolver un problema en el que se puede producir un error al usar la HttpWebRequest clase para enviar una gran cantidad de datos en un equipo que ejecuta Microsoft .NET Framework.

Versión original del producto: .NET Framework
Número de KB original: 908573

Síntomas

Cuando se usa la HttpWebRequest clase para enviar una gran cantidad de datos mediante una POST solicitud o PUT , la solicitud podría producir un error en un equipo que ejecuta .NET Framework. Además, puede recibir un mensaje de error de memoria insuficiente o tiempo de espera agotado.

Si no recibe un mensaje de error de memoria insuficiente o de tiempo de espera, es posible que observe que la aplicación que usa la HttpWebRequest clase usa una gran cantidad de recuerdos. Cuando se usa Monitor de rendimiento para supervisar la aplicación que usa la HttpWebRequest clase , el recuento de bytes privados seguirá aumentando a medida que se envíen los datos. Por lo tanto, también puede experimentar un rendimiento lento en el equipo y en otras aplicaciones porque se produce un aumento de la memoria y el uso de recursos.

Nota:

La cantidad de datos que se pueden cargar de forma predeterminada variará según la memoria y los recursos disponibles en el equipo.

Causa

Este problema se produce porque .NET Framework almacena en búfer los datos salientes de forma predeterminada cuando se usa la HttpWebRequest clase .

Solución alternativa

Para solucionar este problema, establezca la propiedad en HttpWebRequest.AllowWriteStreamBuffering false.

Error causado por la solución alternativa

Puede recibir un mensaje de error como el siguiente ejemplo cuando establece la propiedad en HttpWebRequest.AllowWriteStreamBuffering false:

Esta solicitud requiere almacenar en búfer los datos para que la autenticación del redireccionamiento se realice correctamente.

Para enviar correctamente una gran cantidad de datos mediante una POST solicitud o PUT cuando la HttpWebRequest.AllowWriteStreamBuffering propiedad se establece en false, use uno de los métodos siguientes, en función del método de autenticación que quiera usar.

Autenticación anónima

Si el servidor web está configurado para usar la autenticación anónima, establezca la HttpWebRequest.AllowWriteStreamBuffering propiedad en false. No se necesitan otros cambios.

Autenticación básica

Si el servidor web de Internet Information Services (IIS) está configurado para usar la autenticación básica y puede establecer la HttpWebRequest.AllowWriteStreamBuffering propiedad en false, debe enviar una HEAD solicitud para autenticar previamente la conexión antes de enviar la POST solicitud o PUT . También debe establecer la propiedad en HttpWebRequest.PreAuthenticate true. A continuación, envíe la POST solicitud o PUT y, a continuación, reciba la respuesta. Para ello, use código como el siguiente ejemplo de código.

public void test(Uri URL)
{
    HttpWebRequest WRequest;
    HttpWebResponse WResponse;
    //preAuth the request
    // You can add logic so that you only pre-authenticate the very first request.
    // You should not have to pre-authenticate each request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = new NetworkCredential(user, password);
    WRequest.PreAuthenticate = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "HEAD";
    WRequest.Timeout = 10000;
    WResponse = (HttpWebResponse)WRequest.GetResponse();
    WResponse.Close();
    // Make the real request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = new NetworkCredential(user, password);
    WRequest.PreAuthenticate = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "POST";
    WRequest.AllowWriteStreamBuffering = false;
    WRequest.Timeout = 10000;
    FileStream ReadIn = new FileStream("c:\\testuploadfile.txt", FileMode.Open, FileAccess.Read);
    ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
    WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
    Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
    int DataRead = 0;
    Stream tempStream = WRequest.GetRequestStream();
    do
    {
        DataRead = ReadIn.Read(FileData,0,2048);
        if (DataRead > 0) //we have data
        {
            tempStream.Write(FileData,0,DataRead);
            Array.Clear(FileData,0, 2048); // Clear the array.
        }
    } while (DataRead > 0);

    WResponse = (HttpWebResponse)WRequest.GetResponse();
    // Read your response data here.
    // Close all streams.
    ReadIn.Close();
    tempStream.Close();
    WResponse.Close();
}

Nota:

En función de cómo se diseñe la aplicación, es posible que no tenga que autenticar previamente cada solicitud mediante el envío de una HEAD solicitud.

Autenticación integrada de Windows

Puede configurar un equipo en el que IIS esté instalado para responder mediante Negotiate o Windows Challenge/Response (NTLM) autenticación de Windows. Si IIS está configurado para usar Negotiate para autenticación de Windows, el cliente puede usar Kerberos o NTLM para autenticarse. Si IIS está configurado para usar la autenticación NTLM, solo se puede usar la autenticación NTLM y no se admite la autenticación Kerberos.

Si se usa Negotiate with Kerberos authentication (Negociar con autenticación Kerberos), use la siguiente solución alternativa. La solución alternativa producirá un error si se usa NTLM.

Negociación con la autenticación Kerberos

Si el servidor web IIS está configurado para usar la autenticación Negotiate y debe establecer la HttpWebRequest.AllowWriteStreamBuffering propiedad en false, debe enviar una solicitud HEAD para autenticar previamente la conexión antes de enviar la solicitud POST o PUT. También puede establecer la propiedad en HttpWebRequest.PreAuthenticate true. Además, es posible que tenga que establecer la HttpWebRequest.UnsafeAuthenticatedConnectionSharing propiedad en true. A continuación, envíe la solicitud POST o PUT y, a continuación, reciba la respuesta. Para ello, puede usar código similar al ejemplo de código siguiente.

Nota:

Esta solución alternativa producirá un error si el cliente no puede usar Kerberos con la autenticación Negotiate. También debe asegurarse de que la HttpWebRequest.KeepAlive propiedad está establecida en true. De forma predeterminada, la configuración de la HttpWebRequest.KeepAlive propiedad es true. La lógica para la autenticación Kerberos y básica es casi la misma.

public void test(Uri URL)
{
    HttpWebRequest WRequest;
    HttpWebResponse WResponse;
    CredentialCache myCredCache = new CredentialCache();
    myCredCache.Add(URL,"Negotiate",(NetworkCredential) CredentialCache.DefaultCredentials);
    // Pre-authenticate the request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = myCredCache;
    // This property must be set to true for Kerberos authentication.
    WRequest.PreAuthenticate = true;
    // Keep the connection alive.
    WRequest.UnsafeAuthenticatedConnectionSharing = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "HEAD";
    WRequest.Timeout = 10000;
    WResponse = (HttpWebResponse)WRequest.GetResponse(); 
    WResponse.Close();
    // Make the real request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = myCredCache;
    // This property must be set to true for Kerberos authentication.
    WRequest.PreAuthenticate = true;
    // Keep the connection alive.
    WRequest.UnsafeAuthenticatedConnectionSharing = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "POST";
    WRequest.AllowWriteStreamBuffering = false;
    WRequest.Timeout = 10000;
    FileStream ReadIn = new FileStream("c:\\testuploadfile.txt ", FileMode.Open, FileAccess.Read);
    ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
    WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
    Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
    int DataRead = 0;
    Stream tempStream = WRequest.GetRequestStream();
    do
    {
        DataRead = ReadIn.Read(FileData,0,2048);
        if (DataRead > 0) // We have data.
        {
            tempStream.Write(FileData,0,DataRead);
            Array.Clear(FileData,0, 2048); // Clear the array.
        }
    }while(DataRead > 0);

    WResponse = (HttpWebResponse)WRequest.GetResponse(); 
    // Read your response data here.
    // Close all streams
    ReadIn.Close();
    tempStream.Close();
    WResponse.Close();
}

Nota:

En función de cómo se diseñe la aplicación, es posible que no tenga que autenticar previamente cada solicitud mediante el envío de una solicitud HEAD.

Autenticación NTLM

Si el servidor web IIS también está configurado para usar la autenticación NTLM con autenticación Windows-Integrated y debe establecer la HttpWebRequest.AllowWriteStreamBuffering propiedad en false, puede establecer el tipo de autenticación en NTLM en el código de cliente. Después de configurar IIS para usar la autenticación Negotiate y NTLM y establecer el tipo de autenticación en NTLM en el código de cliente, puede configurar cómo IIS controla las solicitudes de autenticación estableciendo la AuthPersistSingleRequest propiedad en la metabase de IIS en false.

Nota:

Para obtener más información sobre cómo configurar IIS para admitir la autenticación Negotiate y NTLM, consulte la sección Referencias .

También debe enviar una HEAD solicitud para autenticar previamente la conexión antes de enviar la POST solicitud y establecer la HttpWebrequest.UnsafeAuthenticatedConnectionSharing propiedad en true. A continuación, establezca la HttpWebRequest.PreAuthenticate propiedad en false. Por último, envíe la POST solicitud o PUT y, a continuación, reciba la respuesta. Para ello, use código similar al ejemplo de código siguiente.

public void test(Uri URL)
{
    HttpWebRequest WRequest;
    HttpWebResponse WResponse;
    CredentialCache myCredCache = new CredentialCache();
    myCredCache.Add(URL,"NTLM",(NetworkCredential) CredentialCache.DefaultCredentials);
    // Pre-authenticate the request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = myCredCache;
    // For NTLM authentication, you must set the following property to true
    // so the connection does not close.
    WRequest.UnsafeAuthenticatedConnectionSharing = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "HEAD";
    WRequest.Timeout = 10000;
    WResponse = (HttpWebResponse)WRequest.GetResponse(); 
    WResponse.Close();
    // Make the real request.
    WRequest = (HttpWebRequest)HttpWebRequest.Create(URL);
    // Set the username and the password.
    WRequest.Credentials = myCredCache;
    // For NTLM authentication, you must set the following property to true
    // so the connection does not close.
    WRequest.UnsafeAuthenticatedConnectionSharing = true;
    WRequest.UserAgent = "Upload Test";
    WRequest.Method = "POST";
    WRequest.AllowWriteStreamBuffering = false;
    WRequest.Timeout = 10000;
    FileStream ReadIn = new FileStream("c:\\ testuploadfile.txt", FileMode.Open, FileAccess.Read);
    ReadIn.Seek(0, SeekOrigin.Begin); // Move to the start of the file.
    WRequest.ContentLength = ReadIn.Length; // Set the content length header to the size of the file.
    Byte[] FileData = new Byte[ReadIn.Length]; // Read the file in 2 KB segments.
    int DataRead = 0;
    Stream tempStream = WRequest.GetRequestStream();
    do
    {
        DataRead = ReadIn.Read(FileData,0,2048);
        if (DataRead > 0) // We have data.
        {
            tempStream.Write(FileData,0,DataRead);
            Array.Clear(FileData,0, 2048); // Clear the array.
        }
    }while(DataRead > 0);

    WResponse = (HttpWebResponse)WRequest.GetResponse();
    // Read your response data here.
    // Close all streams.
    ReadIn.Close();
    tempStream.Close();
    WResponse.Close();
}

Nota:

En función de cómo se diseñe la aplicación, es posible que no tenga que autenticar previamente cada solicitud mediante el envío de una HEAD solicitud.

Referencias