Für den Zugriff auf REST-basierte Services bietet WCF WebAPI mit der Klasse HttpClient eine Erleichterung für Entwickler. In den Genuss dieser Neuerung kommt man, indem man über NuGet die WebAPI ins Client-Projekt lädt.
Asynchronität
WebClient ist in Hinblick auf Asynchronität entworfen, was man unter anderem daran bemerkt, dass sämtliche Methoden, welche zum Übertragen von Daten zur Verfügung stehen, lediglich in einer aynchronen Variante, welche als Ergebnis einen Task liefert, angeboten werden. Über die Eigenschaft Result von Task kommt man dann zum Ergebnis, wobei diese Eigenschaft so lange blockiert, bis dieses tatsächlich zur Verfügung steht. Dies korreliert mit dem Mindset, welches mit Windows 8 und Metro Einzug halten soll: Alles was länger als x ms dauert, wird zur Verbesserung des Antwortverhaltens der Anwendung asynchron.
Anfragen absenden
Für die vier populärsten HTTP Verben steht jeweils eine Methode zur Verfügung: GetAsync, PostAsync, PutAsync und DeleteAsync. Übergeben wird an diese Methoden die URL der anzusprechenden Ressource. Überladungen erlauben auch die Übergabe eines CancellationTokens zum Abbruch der angestoßenen asynchronen Aktion sowie das Angeben eines Wertes des enums HttpCompletionOption. Mit letzterem wird festgelegt, ob lediglich der Header (HttpCompletionOption.ResponseHeadersRead) oder auch die Nutzdaten (HttpCompletionOption.ResponseContentRead) der Antwort zu lesen sind.
Daten senden
Der Einsatz von PostAsync und PutAsync erwartet die Übergabe von zu sendenden Nutzdaten in der Form einer Instanz des Typs HttpContent (siehe Methode PostBuchungDemo, unten). Hierbei handelt es sich um eine abstrakte Klasse, deren Subklassen verschiedene Darstellungsformen von Ressourcen repräsentieren. So kann zum Beispiel an ObjectContent, wie in der unten abgebildeten Methode PostBuchungDemo ersichtlich, ein zu serialisierendes Objekt übergeben werden. Alternativen dazu sind u. a. die selbstsprechenden Klassen StringContent und StreamContent. Hierbei ist darauf zu achten, dass jene HTTP-Header, die den Inhalt betreffen dem ObjectContent bekannt zu geben sind; die restlichen können über die Eigenschaft DefaultRequestHeaders von HttpClient festgelegt werden. Wie der Name dieser Eigenschaft schon vermuten lässt, werden diese Header-Einträge standardmäßig bei jedem Aufruf, der über den jeweiligen HttpClient erfolgt, herangezogen. Diese logische Aufteilung dürfte für HTTP-Kenner nicht gerade intuitiv erscheinen, da sämtliche HTTP-Header gemeinsam und ohne Gruppierung übertragen werden.
Die volle Kontrolle
Möchte man die volle Kontrolle über die zu sendenden Anfragen haben, kann die Methode SendAsync bemüht werden (siehe Methode SendBuchungDemo, unten). In diesem Fall kann man von den vier Verben GET, POST, PUT und DELETE abweichen und auch Header-Einträge für eine bestimmte Anfrage setzen.
Fazit
Auch wenn die betrachtete API derzeit noch ein wenig Wortreich ist und ab und an ein paar Convenience-Methoden schön wären, bietet diese sich noch in Entwicklung befindliche API mehr Komfort als die bis dato zur Verfügung gestandenen Alternativen. Man muss sich nicht mehr um das Serialisieren und Deserialisieren selber kümmern, es werden out-of-the-box die wichtigsten Darstellungsformen, wie JSON, XML oder URL-Encodet Strings, unterstützt und man erhält Unterstützung beim Definieren der Header durch die API.
private static void GetHotelsDemo()
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client
.GetAsync("http://localhost:5721/hotels?$filter=Sterne ge 4",HttpCompletionOption.ResponseContentRead)
.Result;
if (!response.IsSuccessStatusCode)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Fehler: " + response.StatusCode + " "
+ response.Content.ReadAsStringAsync().Result);
Console.ReadLine();
}
var hotels = response
.Content
.ReadAsOrDefaultAsync<List<Hotel>>()
.Result;
foreach (var hotel in hotels)
{
Console.WriteLine(hotel.ToString());
}
}
private static void PostBuchungDemo()
{
HttpClient client = new HttpClient();
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("bar"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("x-styrian"));
client.DefaultRequestHeaders.AcceptLanguage.Add(new StringWithQualityHeaderValue("de"));
HotelBuchung buchung = new HotelBuchung { HotelId = 2, Vorname = "Peter", Nachname="Silie" };
HttpContent content = new ObjectContent(typeof(HotelBuchung), buchung);
content.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType;
HttpResponseMessage response = client
.PostAsync("http://localhost:5721/hotels/3/buchungen", content)
.Result;
if (!response.IsSuccessStatusCode)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Fehler: " + response.StatusCode + " "
+ response.Content.ReadAsStringAsync().Result);
Console.ReadLine();
}
Console.WriteLine("Status: " + response.StatusCode);
Console.WriteLine("Content: " + response.Content.ReadAsStringAsync().Result);
}
private static void SendBuchungDemo()
{
HttpClient client = new HttpClient();
HotelBuchung buchung = new HotelBuchung { HotelId = 2, Vorname = "Peter", Nachname = "Silie" };
HttpContent content = new ObjectContent(typeof(HotelBuchung), buchung);
content.Headers.ContentType = JsonMediaTypeFormatter.DefaultMediaType;
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, "http://localhost:5721/hotels/3/buchungen");
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("bar"));
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("x-styrian"));
request.Headers.AcceptLanguage.Add(new StringWithQualityHeaderValue("de"));
request.Content = content;
HttpResponseMessage response = client
.SendAsync(request)
.Result;
if (!response.IsSuccessStatusCode)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine("Fehler: " + response.StatusCode + " "
+ response.Content.ReadAsStringAsync().Result);
Console.ReadLine();
}
Console.WriteLine("Status: " + response.StatusCode);
Console.WriteLine("Content: " + response.Content.ReadAsStringAsync().Result);
}