Jak wysłać formularz metodą POST z aplikacji Windows Forms i uwierzytelnić sesję

13-lut-2011

C# posiada metody pozwalające na ściągnięcie wskazanej strony internetowej i jej dalsze przetworzenie lub np zapisanie na dysku. Jak wiadomo, dialog z serwerami www polega zazwyczaj na wysyłaniu do nich żądań (request) i otrzymywaniu od nich odpowiedzi (response). Kiedy klient otrzyma odpowiedź na swoje żądanie, może przetworzyć kod HTML otrzymanej strony i na jego podstawie zażądać kolejnych stron. Zresztą w identyczny sposób działa przeglądarka internetowa. Żeby przeczytać ten artykuł musiałeś wystosować żądanie i otrzymałeś odpowiedź. Patrząc na tę stronę (przetwarzając ją) możesz podjąć decyzję o skierowaniu nowego żądania do serwera poprzez kliknięcie na inny odnośnik znajdujący się na tej stronie.

Wspomniane metody języka C# pozwalające na taką pracę ze stronami www to WebRequest i WebResponse. Na ich bazie zdefiniowano także klasy HttpWebRequest i HttpWeb Response, poszerzające klasę o kilka specyficznych dla protokołu HTTP właściwości.

Sprawa jednak się nieco komplikuje, jeżeli serwer www dorzuca do przekazywanej nam wiadomości inne dodatki np ciasteczka (cookies) czy identyfikator sesji PHP. Może być tak, że kiedy po swoim żądaniu otrzymasz odpowiedź, to realizacja kolejnego żądania, będzie się wiązać z przekazaniem takich przesłanych nam „mimochodem” elementów.

Poniżej przedstawiam proces o następującym przebiegu:

  1. Wczytanie strony zawierającej formularz logowania i odczyt zmiennej sesji (w ciasteczku)
  2. Przesłanie metodą POST danych logowania. Przesyłane dane zawierają przekazane nam ciasteczko
  3. Odczyt innej strony, do której dostęp początkowo (w kroku 1) nie był możliwy, bo użytkownik nie był zalogowany i nie miał do niej dostępu.

Kod programu zostanie teraz szczegółowo omówiony

                Uri uri = new Uri(http://www.jakislink.pl/logowanie.html);
                string html;
                string loginData = „login=” + vNazwaUzytkownika + „&password=” + vHaslo;

 te linijki przygotowały nas właściwie tylko na cały proces. Adres URI będzie używany w dalszych żądaniach. Właściwie to żądania te przyjmują zarówno adres w postaci URL, jak i URI. W typ przykładzie decydujemy się na użycie URI. Tutaj zakładamy też, że wszystki żądania będą kierowane do tego samego URI. Zmienna html będzie służyła do przechowania kodu HTML pobranego w ostatnim kroku. loginData zaś łączy ciąg znaków zawierający nazwę użytkowkia i hasła.

                //Żądanie sesji
                HttpWebRequest sessionRequest = (HttpWebRequest)WebRequest.Create(uri);
                sessionRequest.CookieContainer = new CookieContainer();
                CookieContainer cookies = sessionRequest.CookieContainer;
                HttpWebResponse sessionResponse = (HttpWebResponse)sessionRequest.GetResponse();
                sessionResponse.Close(); // bardzo ważne!

Tutaj tworzymy obiekt sessionRequest. Obiekt będzie kierował żądanie do strony z określonyhm wcześniej URI. Ponieważ spodziwamy się uzyskania ciasteczek, w obiekcie tym tworzymy nowy kontener na pliki cookies. Ponieważ do tego kontenera mogą za moment trafić ciasteczka, trzeba ten obiekt zapamiętać. Służy do tego zmienna cookies. Kiedy na rzecz sessionRequest wywołamy metodę GetResponse() otrzymujemy obiekt odpowiedzi – sessionResponse. Z obiektem tym można oczywiście pracować, ale ważne też jest, aby na końcu obiekt Response zamknąć. Nie zamykając response zmienna cookies nie będzie miała określonej zawartości.

Jak na razie mamy więc tylko kontener ciasteczek, a w nim PHPSESSIONID w cookies,  który zaraz wykorzystamy:

                //Logowanie – w oparciu o podany numer sesji
                HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(uri);
                req.CookieContainer = cookies;
                req.Method = „POST”;
                req.ContentType = „application/x-www-form-urlencoded”;
                ASCIIEncoding encoding = new ASCIIEncoding();
                byte[] loginDataBytes = encoding.GetBytes(loginData);
                req.ContentLength = loginDataBytes.Length;
                Stream stream = req.GetRequestStream();
                stream.Write(loginDataBytes, 0, loginDataBytes.Length);
                stream.Close();
                HttpWebResponse res = (HttpWebResponse)req.GetResponse();
                res.Close();

Tworzymy nowe żądanie, którego celem jest zalogowanie nas do serwera. Do tego żądania przypisujemy otrzymany przed momentem kontener cookies. Tutaj należy też określić metodę przesłania danych, typ przesyłanych danych, kodowanie. Parametry zawierające dane użytkownika są następnie zamieniane w tablicę znaków, a potem jest wyliczana długość tej tablicy. Tę tablicę zapisuje się do strumienia związanego z żądaniem. Zaraz po zapisaniu danych można poprosić o odpowiedź.

Najważniejsze rzeczy zadziały się teraz prawdopodobnie na serwerze. Do naszego numeru sesji, który pamiętamy w cookie, na serwerze zapisało się jakim jesteśmy użytkownikiem. Jesteśmy więc już zidentyfikowani! Przesłana nam strona www zawiera zazwyczaj kod powodujący ponowne załadowanie strony www. Stronę ładuje się ponownie, gdyż zdecydowanie zmienił się stan użytkownika z niezalogowanego stał się zaufanym! Zwrócona nam strona www nie jest więc zbyt ciekawa, stąd teź natychmiast ją zamykamy.

                //Pobranie strony z zawartością
                req = (HttpWebRequest)HttpWebRequest.Create(uri);
                req.CookieContainer = cookies;
                req.Method = „GET”;
                res = (HttpWebResponse)req.GetResponse();
                StreamReader sr = new StreamReader(res.GetResponseStream());
                html = sr.ReadToEnd();
                res.Close();

Teraz już właściwie następuje finał. Tworzymy nowe żądanie req. Żądanie musi operować na tych samych cisateczkach co w poprzednich krokach. Zwykłe pobranie strony nie wymaga metody POST, stąd też tutaj używamy metody GET.  Po wywołaniu metody req.GetResponse(), można już odczytać ze strumienia z odpowiedzią zawartość strony www i dalej ją przetworzyć (tutaj została umieszczona w zmiennej html.

Pomysł został zaczerpnięty z niemieckojęzycznej strony:

http://www.mycsharp.de/wbb2/thread.php?threadid=50145

Komentarze są wyłączone

Autor: Rafał Kraik