Ostatnio musiałem napisać aplikację w której aplikacja .Net współpracuje z aplikacją napisaną w PHP za pośrednictwem gołego http.
Sprawa nie jest szczególnie złożona jednak jak się później okazało nie jest taka prosta jak myślałem. Fragmenty kodu zawarte w Blogu pochodzą z przykładowego programu dostępnego do pobrania na końcu postu.
To co było proste:
Komunikacja PHP z dowolną inną platformą odbywa się za pomocą protokołu HTTP który jest ogólnie dobrze opisany np. tutaj. Dodatkowo w większości platform istnieją dedykowane biblioteki obsługujące w mniejszym lub większym stopniu komunikację przez http, a biblioteka standardowa .Net nie jest pod tym względem wyjątkiem(tfu!).
Komunikację za pomocą http ułatwiają przynajmniej dwie klasy HttpWebRequest i WebClient. Klasa WebClient ma w moim mniemaniu znacznie bardziej intuicyjny interfejs i to jej będę się starał używać.
Wywołanie GET:
Wywołanie GET jest odwołaniem się do strony www równoważnym z wpisaniem adresu w przeglądarce internetowej, parametry do serwera wysyła się dopisując po adresie strony znak zapytania i wartości tych parametrów w formacie parametr1=wartosc1¶metr2=wartosc2¶metr3=wartosc3. Ograniczeniem tej metody jest fakt iż długość adresu strony wraz z wartościami parametrów nie może przekraczać 256znaków.
Metody tej nie powinno się także stosować na stronach w celu przekazywania danych wrażliwych (jak np. login i hasło użytkownika).
Do zbudowania łańcucha połączeniowego można użyć klasy UriBuilder, jest to klasa która na podstawie fragmentów adresu potrafi złożyć pełną ścieżkę do pliku/katalogu także na serwerze www.
Klasą która się przydaje jest także HttpUtility posiadająca metodę UrlEncode zwalniającą programistę z wstawiania odpowiednich znaków ucieczki w wysyłanym zapytaniu do serwera, niestety klasa ta znajduje się w przestrzeni nazw System.Web w bibliotece o tej samej nazwie, biblioteka ta zaś nie znajduje się w Wersji Client Profile platformy .Net. Tak więc jeżeli klasy tej chcemy użyć w projekcie Windows Forms lub WPF musimy dokonać następujących zmian w projekcie:
1) Zapisanie wszystkich niezapisanych plików.
2) W Solution Explorze klikamy prawym przyciskiem myszy na projekt i wybieramy Properties
3) W zakładce Application znajdujemy Combobox Target platform i wybieramy w nim .Net Framework 4.0
4) Zapisujemy wszysto i zamykamy właściwości projektu
5) Klikamy prawym przyciskiem w katalog refereces w projekcie i wybieramy AddReference
6) Przechodzimy na zakładkę .Net i wybieramy System.Web
7) Zatwierdzamy dialog i możemy używać klasy System.Web.HttpUtility
Najprostszy kod pobierający treść strony www może wyglądać tak.
WebClient client = new WebClient(); // Przy niektórych serwerach aplikacja powinna się przedstawić //client.Headers.Add("user-agent", "PHP and dotNet"); UriBuilder queryBuilder = new UriBuilder(tbServer.Text + "helloGet.php"); queryBuilder.Query = string.Format("name={0}", HttpUtility.UrlEncode(tbName.Text)); string result = client.DownloadString(queryBuilder.Uri); MessageBox.Show(this, result);
plik helloGet.php zawiera chyba najprostrzą możliwą obsługę zapytania GET:
<?php echo 'Witaj '.$_GET['name']; ?>
Pobranie plików z serwera:
Zadanie trochę mniej związane z PHP, aczkolwiek pojawiające się w pracy z http to pobranie pliku z serwera http. Tutaj klasa WebClient oferuje dwie możliwości.
Metoda DownloadFile podaje się adres pliku i nazwę pliku nic prostszego
using (SaveFileDialog sfd = new SaveFileDialog()) { sfd.Filter = "Pliki jpeg|*.jpg"; if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { WebClient client = new WebClient(); client.DownloadFile("http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg", sfd.FileName); } }
Metoda DownloadData zwraca tablicę bajtów składających się na plik metoda może być przydatna gdy pobrane dane chcemy tylko obrobić bez zapisywania ich na dysku. Np. wyświetlić obrazek w pictureBoxie.
WebClient client = new WebClient(); byte[] buffer = client.DownloadData("http://www.programy.witraze-plocin.pl/wp-content/uploads/2010/04/cycki226jo5.jpg"); MemoryStream stream = new MemoryStream(buffer); // Kwestie techniczne if (pbCycki.Image != null) { Image oldImage = pbCycki.Image; pbCycki.Image = null; oldImage.Dispose(); } pbCycki.Image = Image.FromStream(stream);
Wadą tej metody jest spore zajęcie pamięci ponieważ cały plik jest wgrywany do RAMu, dodatkowo aplikacja musi pobrać cały plik i dopiero wtedy go obrobić. Przypadkiem gdy żadna z powyższych metod nie zastosowania jest pobranie dużego pliku z serwera i zapisanie go do strumienia nie będącego plikiem(np. wysłać portem COM do jakiegoś urządzenia). W tym przypadku należy wykorzystać metodę WebRequest.
private void FromHttpToStream(Stream outStream, string url) { WebClient client = new WebClient(); client.OpenRead(url).CopyTo(outStream); }
Wywołanie POST:
Wysłanie zapytania POST do serwera http przy pomocy klasy HttpClient jest dosyć niskopoziomowe. Ponieważ należy wykonać następujące kroki:
1) Dodać do nagłówka wiadomości informację o rodzaju przesyłanej treści "content-type: application/x-www-form-urlencoded"
2) Sformatować treść wysyłanej wiadomości przy UrlEncode
3) Zakodować informację przy pomocy kodowania ASCII i wysłać na serwer.
4) Rozkodować przesłaną wiadomość przy pomocy kodowania użytego na stronie.
WebClient client = new WebClient(); // Przy niektórych serwerach aplikacja powinna się przedstawić //client.Headers.Add("user-agent", "PHP and dotNet"); client.Headers.Add("Content-Type", "application/x-www-form-urlencoded"); byte[] buffer = Encoding.ASCII.GetBytes(string.Format("name={0}", HttpUtility.UrlEncode(tbName.Text))); buffer = client.UploadData(tbServer.Text + "helloPost.php", "POST", buffer); string result = Encoding.UTF8.GetString(buffer, 0, buffer.Length); MessageBox.Show(this, result);
Upload plików:
Upload plików też jest dość prosty klasa WebClient posiada metodę UploadFile, która jako parametry pobiera nazwę wysyłanego pliku i adres pliku do którego wysyłana jest zawartość pliku.
using (OpenFileDialog ofd = new OpenFileDialog()) { ofd.Filter = "Pliki jpeg|*jpg"; if (ofd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { WebClient client = new WebClient(); client.UploadFile(tbServer.Text + "uploadFile.php", ofd.FileName); } }
Po stronie serwera wysyłany plik jest opisany w tabeli $_FILES w polu 'file' obsługa pliku. Przykładowa obsługa może wyglądać następująco:
<?php $tempFile = $_FILES['file']['tmp_name']; copy($tempFile, 'uploaded.jpg'); unlink($tempFile); ?>
Pliki z kodem przykładowego programu są do pobrania tutaj.