2010-08-10 16 views
6

Estoy implementando un método de acción proxy que reenvía la solicitud web entrante y la reenvía a otra página web, agregando algunos encabezados. El método de acción funciona para las solicitudes GET, pero aún tengo problemas para reenviar la solicitud POST entrante.Copiando Http Request InputStream

El problema es que no sé cómo escribir correctamente el cuerpo de la solicitud en la secuencia de solicitud HTTP saliente.

Aquí hay una versión abreviada de lo que tengo hasta ahora:

//the incoming request stream 
var requestStream=HttpContext.Current.Request.InputStream; 
//the outgoing web request 
var webRequest = (HttpWebRequest)WebRequest.Create(url); 
... 

//copy incoming request body to outgoing request 
if (requestStream != null && requestStream.Length>0) 
      { 
       long length = requestStream.Length; 
       webRequest.ContentLength = length; 
       requestStream.CopyTo(webRequest.GetRequestStream())      
      } 

//THE NEXT LINE THROWS A ProtocolViolationException 
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse()) 
       { 
        ... 
       } 

Tan pronto como me llamo GetResponse en la solicitud HTTP saliente, tengo la siguiente excepción:

ProtocolViolationException: You must write ContentLength bytes to the request stream before calling [Begin]GetResponse. 

I No entiendo por qué sucede esto, ya que requestStream.CopyTo debería haberse ocupado de escribir la cantidad correcta de bytes.

Cualquier sugerencia sería muy apreciada.

Gracias,

Adrian

+1

pregunta relacionada - http://stackoverflow.com/questions/226784/how-to-create-a-simple-proxy-in-c –

+0

@James Manning: Gracias por el enlace, pero estoy mucho más allá de eso. Mi proxy funciona bien para todo tipo de solicitudes GET. Es solo el cuerpo de la solicitud POST que todavía me está dando problemas. –

+0

¿Ha intentado llamar a Stream.Flush() en la ruta devuelta por webRequest.GetRequestStream() antes de proceder a llamar a webRequest.GetResponse()? –

Respuesta

13

Sí, .Net es muy fastidioso sobre esto. La forma de resolver el problema es para que ambas operaciones y cierren la transmisión. En otras palabras:

Stream webStream = null; 

try 
{ 
    //copy incoming request body to outgoing request 
    if (requestStream != null && requestStream.Length>0) 
    { 
     long length = requestStream.Length; 
     webRequest.ContentLength = length; 
     webStream = webRequest.GetRequestStream(); 
     requestStream.CopyTo(webStream); 
    } 
} 
finally 
{ 
    if (null != webStream) 
    { 
     webStream.Flush(); 
     webStream.Close(); // might need additional exception handling here 
    } 
} 

// No more ProtocolViolationException! 
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse()) 
{ 
    ... 
} 
+0

Great Post Brian. Funciona de maravilla. Sin embargo, parece que .CopyTo() solo es compatible con .NET 4.0+ – Sage

+0

Gracias. Esto solucionó mi problema.Una pregunta: ¿Existe algún motivo por el que haya usado Flush and Close en un bloque finally, en lugar de envolver webStream en un bloque de uso? ¿Hay efectos secundarios con respecto a webRequest? – David

+0

Estoy bastante seguro de que el enrojecimiento no cambia nada ... GetRequestStream devuelve una instancia de ConnectStream, y esta clase anula Flush con un método vacío, por lo que llamar a Flush no tiene ningún efecto. –

1

intentan modificar el bloque interior si la declaración

long length = requestStream.Length; 
webRequest.ContentLength = length; 
requestStream.CopyTo(webRequest.GetRequestStream()) 

con

webRequest.Method = "POST"; 
webRequest.ContentLength = requestStream.Length; 
webRequest.ContentType = "application/x-www-form-urlencoded"; 
Stream stream = webRequest.GetRequestStream(); 
requestStream.CopyTo(stream); 
stream.Close(); 
+0

Ya lo tengo (está en la ... parte). Como dije, esta es solo la versión abreviada. Las otras cosas funcionan bien, así que no creo que sea relevante para este problema. –

2

Las obras respuesta @ Brian, sin embargo, encontré que una vez requestStream.CopyTo (corriente) fue llamado, sería disparar mi HttpWebResponse. Esto fue un problema ya que no estaba listo para enviar la solicitud. Por lo tanto, si alguien tiene un problema con el envío de no todos los encabezados de solicitud u otros datos, es porque CopyTo está activando su solicitud.