Postar dados de formulário usando HttpWebRequest

91

Quero postar alguns dados de formulário em um URL especificado que não está dentro do meu próprio aplicativo da web. Ele tem o mesmo domínio, como "domain.client.nl". O aplicativo da web tem um url "web.domain.client.nl" e o url onde desejo postar é "idp.domain.client.nl". Mas meu código não faz nada ... alguém sabe o que estou fazendo de errado?

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
fonte
Possível duplicata: stackoverflow.com/questions/5401501/…
Dariusz
6
Não exatamente uma duplicata, como o outro deseja usar especificamente WebClient.

Respostas:

71

Tanto o nome do campo quanto o valor devem ser codificados por url. o formato dos dados de postagem e da string de consulta são os mesmos

A maneira .net de fazer é algo assim

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

Isso cuidará da codificação dos campos e dos nomes dos valores

user3389691
fonte
10
string postdata = outgoingQueryString.ToString();lhe dará uma string com o valor "System.Collections.Specialized.NameValueCollection".
sfuqua
1
Na verdade, @sfuqua, se você descompilar a fonte de HttpUtility (especificamente aquela em System.Web), verá que ele retorna um tipo NameValueCollection especializado: return (NameValueCollection) new HttpValueCollection (query, false, true, encoding); que converte corretamente a coleção em uma string de consulta. Se você usar o do RestSharp, no entanto, ele não ...
The Senator
Interessante, como eu já tinha visto esse mesmo problema ToString(), embora agora não me lembre se foi durante o uso do RestSharp. Possibilidade definitiva. Obrigado pela correção.
sfuqua
Não ficou imediatamente claro como outgoingQueryStringestava funcionando conforme implícito no código de exemplo. Essas duas perguntas respondem a que - (1) HttpValueCollection e NameValueCollection e (2) Qualquer classe .NET que crie uma string de consulta com pares de valores de chave ou semelhantes .
Kenny Evitt
56

Experimente isto:

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
fonte
6
Prefiro escrever: request.Method = WebRequestMethods.Http.Post;
Totalys
check request.HaveResponse before use response
Marco Fantasia
@MarcoFantasia Se você marcar, request.HaveResponseterá que obter a resposta primeiro. HttpWebResponse response = (HttpWebResponse) request.GetResponse(); if (request.HaveResponse) { ... }
Sim Barry
40

Você está codificando o formulário incorretamente. Você só deve codificar os 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

Eu estava errado. De acordo com a seção 8.2.1 da RFC1866, tanto os nomes quanto os valores devem ser codificados.

Mas para o exemplo dado, os nomes não têm caracteres que precisem ser codificados, portanto, neste caso, meu exemplo de código está correto;)

O código em questão ainda está incorreto, pois codificaria o sinal de igual, que é a razão pela qual o servidor da web não pode decodificá-lo.

Uma maneira mais adequada seria:

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
fonte
Obrigado, mas agora estou recebendo o seguinte erro: O servidor remoto retornou um erro: (412) Falha de pré-condição.
wsplinter
Eu pesquisei muito no Google :) Mas eu consertei, estou fazendo de uma maneira suja. Eu não faço um HttpWebRequest, mas eu construo um formulário html dentro de uma String e o wirte para o navegador (no onload farei um envio).
wsplinter
3
@wsplinter: Ajudaria outros se você documentar a solução para a pré-condição falhou.
jgauffin
@jgauffin: Como faço para postar valores de botão de opção? Suponha que eu tenha 3 botões de opção pertencentes ao mesmo grupo.
Asad Refai
1
Aparentemente, isso não funcionou para mim como esperado, uma vez que UrlEncode altera símbolos como @ para um código que não estava sendo aceito na chamada de API; alterá-lo para usar NameValueCollection como mostrado na resposta de @ user3389691 funciona perfeitamente.
dhruvpatel