Publicar datos de formulario usando HttpWebRequest

91

Quiero publicar algunos datos de formulario en una URL específica que no está dentro de mi propia aplicación web. Tiene el mismo dominio, como "domain.client.nl". La aplicación web tiene una URL "web.domain.client.nl" y la URL en la que quiero publicar es "idp.domain.client.nl". Pero mi código no hace nada ... ¿alguien sabe lo que estoy haciendo mal?

Wouter

StringBuilder postData = new StringBuilder();
postData.Append(HttpUtility.UrlEncode(String.Format("username={0}&", uname)));
postData.Append(HttpUtility.UrlEncode(String.Format("password={0}&", pword)));
postData.Append(HttpUtility.UrlEncode(String.Format("url_success={0}&", urlSuccess)));
postData.Append(HttpUtility.UrlEncode(String.Format("url_failed={0}", urlFailed)));

ASCIIEncoding ascii = new ASCIIEncoding();
byte[] postBytes = ascii.GetBytes(postData.ToString());

// set up request object
HttpWebRequest request;
try
{
    request = (HttpWebRequest)HttpWebRequest.Create(WebSiteConstants.UrlIdp);
}
catch (UriFormatException)
{
    request = null;
}
if (request == null)
    throw new ApplicationException("Invalid URL: " + WebSiteConstants.UrlIdp);

request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = postBytes.Length;
request.UserAgent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)";

// add post data to request
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Flush();
postStream.Close();
wsplinter
fuente
Posible duplicado: stackoverflow.com/questions/5401501/…
Dariusz
6
No es un duplicado, ya que el otro quiere usar específicamente WebClient.

Respuestas:

72

Tanto el nombre del campo como el valor deben estar codificados en URL. el formato de los datos de la publicación y la cadena de consulta son los mismos

La forma de hacer .net es algo como esto

NameValueCollection outgoingQueryString = HttpUtility.ParseQueryString(String.Empty);
outgoingQueryString.Add("field1","value1");
outgoingQueryString.Add("field2", "value2");
string postdata = outgoingQueryString.ToString();

Esto se encargará de codificar los campos y los nombres de los valores.

usuario3389691
fuente
10
string postdata = outgoingQueryString.ToString();le dará una cadena con el valor "System.Collections.Specialized.NameValueCollection".
sfuqua
1
En realidad @sfuqua, si descompila la fuente de HttpUtility (específicamente la de System.Web) verá que devuelve un tipo NameValueCollection especializado: return (NameValueCollection) new HttpValueCollection (consulta, falso, verdadero, codificación); que convierte correctamente la colección en una cadena de consulta. Sin embargo, si usa el de RestSharp, no ...
The Senator
Interesante, ya que había visto este problema exacto ToString(), aunque ahora no puedo recordar si fue mientras usaba RestSharp. Posibilidad definitiva. Gracias por la corrección.
sfuqua
No estaba claro de inmediato cómo outgoingQueryString estaba funcionando, como lo implica el código de ejemplo. Estas dos preguntas responden eso: (1) HttpValueCollection y NameValueCollection y (2) Cualquier clase .NET que haga una cadena de consulta dados pares clave-valor o similares .
Kenny Evitt
57

Prueba esto:

var request = (HttpWebRequest)WebRequest.Create("http://www.example.com/recepticle.aspx");

var postData = "thing1=hello";
    postData += "&thing2=world";
var data = Encoding.ASCII.GetBytes(postData);

request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
request.ContentLength = data.Length;

using (var stream = request.GetRequestStream())
{
    stream.Write(data, 0, data.Length);
}

var response = (HttpWebResponse)request.GetResponse();

var responseString = new StreamReader(response.GetResponseStream()).ReadToEnd();
Parisa Shamsaie
fuente
6
Prefiero escribir: request.Method = WebRequestMethods.Http.Post;
Totalys
Verifique la solicitud.HaveResponse antes de usar la respuesta
Marco Fantasia
Si @MarcoFantasia comprobar request.HaveResponseque tienes que en realidad obtener la respuesta en primer lugar. HttpWebResponse response = (HttpWebResponse) request.GetResponse(); if (request.HaveResponse) { ... }
Sí Barry
41

Está codificando el formulario incorrectamente. Solo debes codificar los valores:

StringBuilder postData = new StringBuilder();
postData.Append("username=" + HttpUtility.UrlEncode(uname) + "&");
postData.Append("password=" + HttpUtility.UrlEncode(pword) + "&");
postData.Append("url_success=" + HttpUtility.UrlEncode(urlSuccess) + "&");
postData.Append("url_failed=" + HttpUtility.UrlEncode(urlFailed));

editar

Estaba equivocado. De acuerdo con la sección 8.2.1 de RFC1866, se deben codificar tanto los nombres como los valores.

Pero para el ejemplo dado, los nombres no tienen ningún carácter que deba codificarse, por lo que en este caso mi ejemplo de código es correcto;)

El código de la pregunta sigue siendo incorrecto, ya que codificaría el signo igual, que es la razón por la que el servidor web no puede decodificarlo.

Una forma más adecuada habría sido:

StringBuilder postData = new StringBuilder();
postData.AppendUrlEncoded("username", uname);
postData.AppendUrlEncoded("password", pword);
postData.AppendUrlEncoded("url_success", urlSuccess);
postData.AppendUrlEncoded("url_failed", urlFailed);

//in an extension class
public static void AppendUrlEncoded(this StringBuilder sb, string name, string value)
{
    if (sb.Length != 0)
        sb.Append("&");
    sb.Append(HttpUtility.UrlEncode(name));
    sb.Append("=");
    sb.Append(HttpUtility.UrlEncode(value));
}
jgauffin
fuente
Gracias, pero ahora recibo el siguiente error: El servidor remoto devolvió un error: (412) Precondición fallida.
wsplinter
Busco mucho en Google :) Pero lo arreglé, lo estoy haciendo de una manera sucia. No hago una HttpWebRequest, pero creo un formulario html dentro de una cadena y lo escribo en el navegador (en la carga, lo enviaré).
wsplinter
3
@wsplinter: ayudaría a otros si documentara la solución a la condición previa fallida.
jgauffin
@jgauffin: ¿Cómo publico valores de botones de opción? Supongamos que tengo 3 botones de opción que pertenecen al mismo grupo.
Asad Refai
1
Aparentemente, esto no funcionó para mí como se esperaba ya que UrlEncode cambia símbolos como @ a un código que no estaba siendo aceptado en la llamada API; cambiarlo para usar NameValueCollection como se muestra en la respuesta de @ user3389691 funciona perfectamente.
dhruvpatel