Opublikuj dane formularza za pomocą HttpWebRequest

91

Chcę wysłać dane formularza do określonego adresu URL, który nie znajduje się w mojej własnej aplikacji internetowej. Ma tę samą domenę, na przykład „domain.client.nl”. Aplikacja internetowa ma adres URL „web.domain.client.nl”, a adres URL, do którego chcę pisać, to „idp.domain.client.nl”. Ale mój kod nic nie robi ..... czy ktoś wie, co robię źle?

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
źródło
Możliwy duplikat: stackoverflow.com/questions/5401501/…
Dariusz
6
Niezupełnie duplikat, ponieważ ten drugi specjalnie chce użyć WebClient.

Odpowiedzi:

72

Zarówno nazwa pola, jak i wartość powinny być zakodowane w postaci adresu URL. format danych postu i ciągu zapytania są takie same

Sposób działania .net jest podobny do tego

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

To zajmie się kodowaniem pól i nazw wartości

user3389691
źródło
10
string postdata = outgoingQueryString.ToString();poda ci łańcuch z wartością "System.Collections.Specialized.NameValueCollection".
sfuqua
1
Właściwie @sfuqua, jeśli zdekompilujesz źródło dla HttpUtility (konkretnie to w System.Web), zobaczysz, że zwraca ono wyspecjalizowany typ NameValueCollection: return (NameValueCollection) new HttpValueCollection (zapytanie, fałsz, prawda, kodowanie); który poprawnie konwertuje kolekcję na ciąg zapytania. Jeśli jednak użyjesz tego z RestSharp, to nie ...
Senator
Interesujące, ponieważ widziałem dokładnie ten problem ToString(), chociaż nie mogę teraz sobie przypomnieć, czy był to podczas korzystania z RestSharp. Zdecydowana możliwość. Dziękuję za poprawienie mnie.
sfuqua
Nie było od razu jasne, jak outgoingQueryStringdziała, jak sugeruje przykładowy kod. Te dwa pytania odpowiadają na to - (1) HttpValueCollection i NameValueCollection oraz (2) Dowolna klasa .NET, która tworzy ciąg zapytania z określoną parą klucz-wartość lub tym podobną .
Kenny Evitt
57

Spróbuj tego:

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
źródło
6
Wolałbym napisać: request.Method = WebRequestMethods.Http.Post;
Totalys
check request.HaveResponse przed użyciem odpowiedzi
Marco Fantasia
@MarcoFantasia Jeśli sprawdzisz request.HaveResponse, musisz najpierw uzyskać odpowiedź. HttpWebResponse response = (HttpWebResponse) request.GetResponse(); if (request.HaveResponse) { ... }
Tak Barry
41

Niepoprawnie kodujesz formularz. Powinieneś tylko zakodować wartości:

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));

edytować

Myliłem się. Zgodnie z RFC1866 sekcja 8.2.1 zarówno nazwy, jak i wartości powinny być kodowane.

Ale dla podanego przykładu nazwy nie mają żadnych znaków, które należy zakodować, więc w tym przypadku mój przykład kodu jest poprawny;)

Kod w pytaniu jest nadal niepoprawny, ponieważ zakodowałby znak równości, co jest powodem, dla którego serwer sieciowy nie może go zdekodować.

Właściwszym sposobem byłoby:

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
źródło
Dzięki, ale teraz pojawia się następujący błąd: Serwer zdalny zwrócił błąd: (412) Błąd warunku wstępnego.
wsplinter
Dużo google :) Ale naprawiłem to, robię to po brudnej drodze. Nie używam HttpWebRequest, ale tworzę formularz html wewnątrz String i zapisuję go w przeglądarce (w załadowaniu zrobię przesyłanie).
wsplinter
3
@wsplinter: Pomogłoby to innym, gdybyś udokumentował niepowodzenie rozwiązania warunku wstępnego.
jgauffin
@jgauffin: Jak zamieścić wartości przycisków opcji? Załóżmy, że mam 3 przyciski opcji należące do tej samej grupy.
Asad Refai
1
Najwyraźniej nie zadziałało to dla mnie całkiem zgodnie z oczekiwaniami, ponieważ UrlEncode zmienia symbole, takie jak @, na kod, który nie był akceptowany w wywołaniu API; zmiana go tak, aby używała NameValueCollection, jak pokazano w odpowiedzi @ user3389691, działa idealnie.
dhruvpatel