VB NET Web API – Währungskurs Service Konsum von Grund auf erklärt

VB NET Web API Konsum – Währungskurse umrechnen

VB NET Web API – Web API Konsum von Grund auf erklärt

Heute erwartet Dich ein ganz besonderer und von vielen Abonnenten gewünschter Beitrag – die Implementierung einer VB NET Web API.

Es handelt sich genauer genommen um die Erstellung eines Clients, Welcher gewünschte Daten aus einer Web-Schnittstelle abrufen kann.

Die Web-API arbeitet dabei nach dem üblichen Standard, Sie gibt JSON-Daten zurück, Welche wir dann noch clientseitig verarbeiten müssen.

Letztendlich ist der Client natürlich nichts anderes als eine VB NET Klasse, bzw. eher gesagt eine Instanz davon.

Auf YouTube ansehen – VB NET Web API

Falls Du die visuelle Darstellung, bzw. Erklärung des Beitrages bevorzugst, kannst Du natürlich auch das dementsprechende Video anschauen.

Ursprung und Idee zum Beitrag – VB NET Web API

Den ursprünglichen Anreiz zum Video habe ich – wie fast immer – durch den eigenen Bedarf dazu gehabt.

Ich baue mir aktuell ein kleines Tool, Welches mir bei meiner Arbeit mit dem Staking von Kryptowährungen helfen soll.

Hier zeige ich Dir mal einen kleinen Ausschnitt, beachte jedoch, dass das Tool hier nicht weiter aufgegriffen wird:

VB.NET Web-API - CakeStats Staking Tool
VB.NET Web-API – CakeStats Staking Tool

Bisher musst ich bei Bedarf immer die einzelnen Werte und Positionen selbst errechnen, nun habe ich dafür letztendlich ein Mini-Tool gebaut.

Bewusst in der unteren, rechten Ecke positioniert, sollte es als eine Art Widget im Hintergrund funktionieren.

Bei Bedarf sollte es mir dann die gesammelten/verarbeiteten Informationen aufbereitet darstellen.

Gedanken zum Start

VB.NET Web API - Start
VB NET Web API – Start

Bei den initialen Gedanken bezüglich des Projektstarts, kann man natürlich einiges einbeziehen.

Ich spreche hier unter anderem von Konzepten wie z. B. die Dependency-Injection, bzw. auch kurz DI genannt.

Als nächstes sollte man sich vermutlich über die restliche Infrastruktur der App Gedanken machen.

Hast Du eventuell das MVVM-Entwurfsmuster für Deine Anwendung eingeplant, oder möchtest Du „quick and dirty“ durchstarten?

In meinem Beitrag über das MVVM-Entwurfsmuster kannst Du mehr darüber erfahren und detailliert nachlesen.

Möchtest Du eventuell grafisch ansprechender arbeiten und ein GUI-Framework wie Mahapps-Metro verwenden?

Dann schaue Dir ggf. folgende Beiträge über Mahapps Metro an: Mahapps Metro Projekt aufsetzen.

Wegfallendes & Veränderungen

Änderungen und Updates in der Softwarebranche
Änderungen und Updates in der Softwarebranche

Viele Entwickler arbeiten bei einem Projekt zuerst einmal an einem „Minimum Viable Product“, kurz MVP.

Die Welt der Programmierung befindet sich in einem permanenten Wandel und nicht anders geht es Deinem Projekt.

Man möchte Kunden, Investoren, oder auch einfach Vorgesetzten ein schnelles und praxisnahes Beispiel des Projektes vorzeigen können.

Oftmals resultiert dies aus bestehendem Projektdruck, anderen wartenden Projekten, ungeduldigen Kunden, usw.

Da es sich hierbei allerdings um ein privates Projekt handelt, habe ich mich für eine hybride, bzw. einfache Lösung entschieden.

Ich habe daher als Basis ein kleines Interface erstellt, wovon sich dann Implementierungen ergeben können.

Offline APIs und Preisgestaltung
Offline APIs und Preisgestaltung

Man weiß nie, welche Webseiten, bzw. Schnittstellen irgendwann ihren Dienst quittieren.

Ebenso weiß man häufig nicht, inwiefern sich Preismodelle verändern, da sich auch kostenlose Modelle der Wirtschaftlichkeit anpassen müssen.

Besonders ehemals kostenlose „Subsciption-Plans“ werden irgendwann kostenpflichtig und bestehende Pläne werden teurer.

Wenn solche Veränderungen eintreten muss bestehende Software im Optimalfall natürlich gut gebaut sein.

Sie muss über eine gute Infrastruktur und Planung verfolgen, um auf eventuelle Änderungen reagieren zu können.

Dabei sollte Sie effizient und einfach umgestellt werden können und erfolgte Änderungen sollten einfach getestet werden können.

Vorbereitungen für die VB NET Web API

Um dieses Tutorial erfolgreich abschließen zu können, musst Du vorher noch einige Vorbereitungen treffen.

Du brauchst im ersten Schritt einen Account für den verwenden Web-Service namens „exchangeratesapi„.

Besuche dafür einfach die Website namens exchangeratesapi.io und erstelle Dir einen kostenlosen Account im „Free Subscription Plan“.

exchangeratesapi.io Registrierung – Free Subscription Plan
exchangeratesapi.io Registrierung – Free Subscription Plan

Der kostenlose Plan von exchangeratesapi.io bietet Dir die Möglichkeit 250 Anfragen im Monat kostenlos zu nutzen.

Wenn Du die Registrierung abgeschlossen und Deinen API-Key erhalten hast, können wir fortfahren.

Los geht’s

Hier starten wir mit der Basis des Codes und setzen die ersten Überlegungen um.

Wie oben schon erwähnt wird es ein Interface als kleine Basis geben:

Public Interface IExchangeRateService

    Function GetExchangeRateAsync(baseCurrency As String, targetCurrency As String) As Task(Of Double)

End Interface

Dieses stellt in der absoluten „easy-to-go“-Version nur eine einzige Funktion bereit – „GetExchangeRateAsync“.

Die Funktion soll uns den Wechselkurs der jeweiligen Basis-Währung in die angegebene ZielWährung liefern.

Wie Du in der Deklaration erkennen kannst, übergeben wir die beiden Währungen als Parameter vom Typ String.

Des Weiteren gibt die Funktion keinen simplen Double-Wert, sondern eine Task mit einem generischen Parameter vom Typ Double zurück.

Ich bevorzuge die asynchrone Arbeitsweise, daher wird die Funktion beispielhaft wie folgt aufgerufen:

Private Async Task LoadExampleDataAsync()
    Dim exchangeRate As Double = Await theService.GetExchangeRateAsync("EUR", "USD")
End Sub

Auch bei der Bezeichnung der Variante sollte man natürlich auf die „Asynchronität“ eingehen.

In der VB NET Welt hat es sich eingebürgert, asynchrone Funktionalitäten mit einem „Async„-Postfix zu kennzeichnen.

Möchte man stattdessen die synchrone Variante verwenden, kann man dafür natürlich auch einfach folgenden Code verwenden:

Private LoadExampleDataSync()
    Dim exchangeRate As Double = theService.GetExchangeRateAsync("EUR", "USD").Wait()
End Sub

Die oben genannte Schnittstelle wird dann von jeweiligen ExchangeRateServices implementiert – vorerst jedoch nur von Einem.

Konkrete Implementierung des Services

Mit dem Status quo gibt es nur eine „Service-Möglichkeit„, also eine konkrete Implementierung des Interfaces.

Dabei handelt es sich um die Web-Dienstleistung der oben genannten Seite, Welche uns als Daten-Lieferant dient.

Selbstverständlich könnten wir auch genauso gut mehrere Anbieter heraussuchen und Diese dann in eigenen Services implementieren.

Im folgenden Bild versuche ich diese Gegebenheit ein wenig besser darzustellen:

VB.NET Web-API - ExchangeRateServiceDiagram
VB.NET Web-API – ExchangeRateServiceDiagram

Klasse erstellen – Für ExchangeRatesApi.io

Erstelle also im nächsten Schritt eine eigene Klasse, in der wir dann beginnen, den Web-Dienst und unser Interface zu implementieren.

Die Bezeichnung bleibt hier natürlich Dir überlassen, man könnte hier den Hersteller des Web-Dienstes in irgendeiner Form einbringen.

Damit würde man ggf. klarer darstellen, dass es sich um die konkrete Implementierung für diese eine Website handelt.

Der Einfachheit halber und mangels anderer Implementierungen belasse ich es bei einem simplen Namen.

Implementiere anschließend das oben gezeigte Interface und Du solltest den gleich folgenden Stand haben.

Tippe dazu einfach „Implements“ in die nächstfolgende Zeile nach dem Klassennamen und gebe den Namen des Interfaces ein.

Danach kannst Du das gewünschte Interface ggf. schon durch Autokorrektur bestätigen.

Public Class ExchangeRateService
    Implements IExchangeRateService

    Public Function GetExchangeRateAsync(baseCurrency As String, targetCurrency As String) As Task(Of Double)
       
    End Function

End Class

Mit dem S aus „SOLID“ im Kopf

Single Reponsibility Principle in SOLID
Single Reponsibility Principle in SOLID

Mit dem „Single Responsibility“-Prinzip, Welches sich bei dem Akronym „SOLID“ hinter dem Buchstaben „S“ verbirgt im Kopf, arbeiten wir weiter an der Klasse.

Die Bedeutung des Prinzips ist letztendlich grob gesagt, dass eine Klasse jeweils nur einen Zweck haben sollte.

Inwiefern viele Entwickler diesen Interpretations-Raum ausschöpfen, oder regulieren, mag erstmal dahingestellt sein.

In unserem Fall hat die Klasse ja z. B. einen API-Key, Welchen Sie verwenden muss, um bei der Web-Schnittstelle authentifiziert zu werden.

Diesen API-Key zu laden, ist allerdings eigentlich nicht die Aufgabe der Klasse selbst, sondern eher einer „Configuration“-like Klasse.

Die „Configuration„-Klasse könnte natürlich auch wieder ein Interface mit „Get“- und „Set“-Methoden implementieren.

Man könnte die Konfiguration im ersten Schritt durch die NET Settings realisieren, danach vielleicht auf Datenbank-, oder gar auf Web-Ebene.

So würde alles schön sauber, separat und wart-, bzw. testbar sein – schön!

Basis-Daten festlegen

Statt der eigenen Konfigurations-Klasse verwenden wir der Einfachheit halber einfach konstante Strings.

Diese definieren dann die benötigten Daten, wie z. B. den API-Schlüssel und die Basis-URL an Welche die Anfragen gesendet werden müssen.

Die Daten selbst verwenden wir dann später, um korrekt formatierte/aufgebaute Anfragen zu erstellen.

Public Const API_KEY As String = "<myApiKey>"
Public Const BASE_URL As String = "https://api.exchangeratesapi.io/v1"

Ein Singleton, Welcher keiner ist – lol

In Vielen verschiedenen Sprachen sollte man Singletons für Zugriffe auf das Web, also durch GET-, POST-, etc. Methoden erstellen.

Es macht keinen Sinn, für jede einzelne Anfrage einen weiteren „HttpClient“ zu erzeugen, sondern eher den Selben erneut zu benutzen.

Soweit ich mich erinnern kann, steht das sogar in der Dokumentation zum Client drin.

Als ich damals mit Java für die Android-Entwicklung gearbeitet habe, war es dort übrigens auch so.

Mittlerweile hat Google glaube ich selbst die Steuerung übernommen, damit man dies praktisch nicht mehr selbst realisieren muss.

Schnell und einfach habe ich hier einfach folgenden Code verwendet:

Imports System.Net.Http

Public Class Http
  
  Public Shared <ReadOnly> Client As HttpClient = New HttpClient()

End Class

Dieser Code sorgt zwar nicht dafür, dass der Client nicht einfach neu gesetzt werden könnte, macht aber denke ich den Part der Wiederverwendung klar.

Setze ggf. einfach einen Readonly-Modifizierer vor den Namen des Clients.

Den Request zusammenbauen – VB NET Web API

ExchangeRatesApi Server Request - VB NET Web API
ExchangeRatesApi Server Request – VB NET Web API

Wir können natürlich nicht einfach irgendwelche Anfragen an irgendwelche URLs schicken, sondern müssen uns irgendwo an den korrekten Aufbau halten.

Die oben definierte Basis-URL dient schonmal wie der Name sagt als Basis für das Ganze.

Im nächsten Schritt müssen wir in der Dokumentation herausfinden, welchen sogenannten Endpunkt wir verwenden müssen.

In unserem Fall ist der korrekte Endpunkt der „/latest„-Endpunkt, Welcher natürlich noch zusätzliche Daten übermittelt bekommen möchte.

Unsere Funktion muss also nun einen GET-Request an den genannten Endpunkt senden.

Aus der Dokumentation geht hervor, dass jeder (gesicherte) Endpunkt unseren API-Key in Form eines QueryParameters übergeben bekommen möchte.

Des Weiteren braucht der Endpunkt die Information, welche Basis-Währung verwendet werden soll.

Standardmäßig kann man im Free-Plan leider nur Euro verwenden, ich werde die Implementierung des Services natürlich trotzdem korrekt gestalten.

Behalte allerdings deshalb im Kopf, dass Du nur EUR“ als Basis-Währung übermitteln kannst und Du sonst vermutlich einen Fehler bekommen wirst.

Alternativ könntest Du den Parameter auch eventuell weglassen und die Web-Schnittstelle würde so automatisch „EURziehen.

Gleich findest Du einen möglichen Aufruf, bzw. eine mögliche Verwendung der bekannten Aspekte.

Zuerst wandle ich die übergebenen Währungen (wie Sie üblich benötigt werden) in Großbuchstaben um (eur -> EUR).

Query-Parameter

Danach mache ich mir den Rückgabewert der ParseQueryString-Funktion der HttpUtility-Klasse zu Nutze.

Daraus bekommen wir eine Art NameValueCollection, ich sage deshalb „Art“, weil es wohl eine davon erbende Variante sein muss.

In Visual Studio selbst, sagt die Dokumentation leider im Tooltip dazu nicht mehr.

Vielleicht übersehe ich auch etwas, aber probiere mal die manuelle Erstellung einer „NameValueCollection“.

Füge anschließend 2-3 Einträge hinzu und gebe Diese dann als String aus und Du wirst einen Unterschied feststellen.

Dim myNVC = new NameValueCollection()
myNVC.Add("entry1", "value1")
myNVC.Add("entry2", "value2")
myNVC.Add("entry3", "value3")
' "normal"
Dim str = myNVC.ToString()
' or with Interpolation
' Dim str = $"{myNVC}"

Im obigen Beispiel bekommst Du folgendes Ergebnis (wenn Du es ausgibst..):

NameValueCollection ToString Aufruf
NameValueCollection ToString Aufruf

Man bekommt also praktisch nur den vollständigen Pfad der Klasse inkl. Namespace zurück.

Wenn Du Dir allerdings den „ToString“-Wert des folgenden Beispiels anschaust, wird die Situation klarer:

Dim API_KEY = "BLA"
Dim baseCurrency = "EUR"
Dim targetCurrency = "USD"

Dim query = HttpUtility.ParseQueryString(String.Empty)
query.Add("access_key", API_KEY)
query.Add("base", baseCurrency)
query.Add("symbols", targetCurrency)

Dim str = query.ToString()
Console.WriteLine(str)
Console.ReadKey()
Query NameValueCollection ToString
Query NameValueCollection ToString

Hier bekommen wir nun die einzelnen Schlüssel- und Wert-Paare wie wir Sie für die URL benötigen zurück.

Die von der „NameValueCollection“ erbende Klasse, Welche wir vermutlich von „HttpUtility.ParseQueryString“ zurückbekommen, wird also die „ToString„-Funktion überschreiben.

Somit sieht also unser erster API aufrufender Code so aus:

Public Function GetExchangeRateAsync(baseCurrency As String, targetCurrency As String) As Task(Of Double)
    baseCurrency = baseCurrency.ToUpper()
    targetCurrency = targetCurrency.ToUpper()

    Dim query = HttpUtility.ParseQueryString(String.Empty)
    query.Add("access_key", API_KEY)
    query.Add("base", baseCurrency)
    query.Add("symbols", targetCurrency)

    Dim apiUrl = $"{BASE_URL}/latest?{query}"
    Dim response = Await Http.Client.GetAsync(apiUrl)
    Dim responseContent = Await response.Content.ReadAsStringAsync()

    ' parsing..

    Return 0
End Function

Setze Dir gerne mal einen Haltepunkt auf den „responseContent“ und schaue Dir das Ergebnis an:

{"success":true,"timestamp":1632925144,"base":"EUR","date":"2021-09-29","rates":{"JPY":129.884571}}

API-Antwort verarbeiten – VB NET Web API

JSON zu NET Objekt umwandeln - VB NET Web API
JSON zu NET Objekt umwandeln – VB NET Web API

Einige werden das obige Format, Welches als Antwort von der API kommt vermutlich bereits kennen und verstehen.

Für andere noch nicht so in der Entwicklung tief verankerte Personen, ist es ggf. ein kryptisches Gebilde.

Bei dem kryptischen Text handelt es sich aber um nichts Geringeres, als die „JavaScript Object Notation“ – kurz JSON.

Sie sieht im Vergleich zum ebenfalls sehr bekannten, aber zugegebenermaßen bereits in die Jahre gekommenen XML-Standard kryptischer aus.

Allerdings ist JSON wesentlich kompakter und für das geschulte Auge auch nicht wesentlich schwieriger zu lesen.

JSON in Objekt umwandeln

Um nun mit unserer Arbeit an der API fortfahren zu können, müssen wir den obigen JSON-String in ein NET-Objekt umwandeln.

Nur so ermöglichen wir uns eine einfache und saubere Arbeit damit, denn wer möchte hier schon manuelle Arbeit mit Funktionen wie z. B. Substring, etc. leisten.

Für die Umwandlung selbst benötigen wir im ersten Schritt erstmal eine Klasse, Welche das in JSON formulierte Objekt auf NET-Ebene widerspiegelt.

Dazu könnten wir einerseits händisch loslegen und die einzelnen Eigenschaften definieren:

Public Class ApiResponse

  Public Property success As Boolean

  Public Property timestamp As Integer

  ' ...

End Class

Oder uns andererseits durch moderne Hilfsmittel in Visual Studio helfen lassen.

Visual Studio besitzt die Funktionalität, einen JSON-String gar automatisiert in passende Klassen umzuwandeln:

JSON String mit Visual Studio in NET Objekt umwandeln - VB NET Web API
JSON String mit Visual Studio in NET Objekt umwandeln – VB NET Web API

JSON String mit Visual Studio in NET Objekt umwandeln

Auch wenn es mit dem aktuellen Stand schon funktionieren würde, den String in ein Objekt (mit Sub-Objekten) umzuwandeln.

Leider kann die API ja mit dynamischen symbolsaufgerufen werden, weshalb ggf. unterschiedliche und nie fixe Properties“ in dem „Rate“-Objekt zurückkommen.

Wenn Dich das nicht weiter stört, könntest Du natürlich die „Rate„-Klasse so ausbauen, dass sämtliche Währungen dort drinnen vertreten wären.

Ich persönlich fände das allerdings nicht so prickelnd, daher bauen wir die „RootObject„-Klasse noch ein wenig um und lassen die „Rates„-Klasse verschwinden.

JSON Parser installieren

Als nächstes installieren wir das bekannte Newtonsoft Json NuGet-Paket, um eine einfache Handhabung mit JSON-Objekten zu erhalten.

Danach passen wir die „RootObject„-Klasse an, indem wir Sie zuerst einmal sinnvoller benennen, also etwa ExchangeRatesApiResponse„.

Anschließend ändern wir den Rückgabetyp der „rates„-Eigenschaft in „JObject“ um, Welches es uns die Arbeit erleichtert.

Nun können wir die „responseContent„-Variable, worin unser JSON-String steckt endlich umwandeln.

Im gleichen Schritt können wir auch noch die „exchangeRate“, also den Wechselkurs, typisiert aus der „rates„-Eigenschaft ziehen.

Die Umwandlung selbst – VB NET Web API

Dazu rufe ich im ersten Schritt die „DeserializeObject„-Funktion der „JsonConvert„-Klasse auf, Welche wir uns mit dem NuGet-Package installiert haben.

Dabei übergebe ich den generischen Parameter „ExchangeRatesApiResponse“, Welcher unseren Ziel-Datentyp für die „Antwort-Klasse“ widerspiegelt.

Schon haben wir den letztendlichen Wechselkurs vorliegen!

Dim apiResponse = JsonConvert.DeserializeObject(Of ExchangeRatesApiResponse)(responseContent)
Dim exchangeRate = apiResponse.rates.Value(Of Double)(targetCurrency)

Ein beispielhafter Aufruf unseres Services innerhalb einer Konsolenanwendung könnte nun final so aussehen:

Module Program

    Sub Main(args As String())
        Dim service = New ExchangeRateService()
        Dim exchangeRate = service.GetExchangeRateAsync("EUR", "JPY").Wait()
        ' do something with exchangeRate..
    End Sub

End Module

Weiterführende Links

Downloads

Hier findest Du wie immer die zugehörigen Downloads zum Beitrag.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.