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.