Zugreifen auf Remotedaten
Tipp
Diese Inhalte sind ein Auszug aus dem eBook „Enterprise Application Patterns Using .NET MAUI“, verfügbar unter .NET Docs oder als kostenlos herunterladbare PDF-Datei, die offline gelesen werden kann.
Zahlreiche moderne webbasierte Lösungen nutzen auf Webservern gehostete Webdienste, um Funktionen für Remoteclientanwendungen bereitzustellen. Die von einem Webdienst verfügbar gemachten Vorgänge bilden eine Web-API.
Client-Apps sollten die vom Webdienst bereitgestellte API nutzen können, ohne Kenntnis darüber zu haben, wie die von der API offengelegten Daten oder Vorgänge implementiert sind. Dazu muss die API allgemeine Standards einhalten, die es einer Client-App und einem Webdienst ermöglichen, sich bezüglich der zu verwendenden Datenformate und der Struktur der zwischen den Client-Apps und dem Webdienst ausgetauschten Daten zu verständigen.
Einführung in Representational State Transfer (REST)
Representational State Transfer (REST) ist ein Architekturstil zur Entwicklung verteilter Systeme, die auf Hypermedia basieren. Ein wichtiger Vorteil des REST-Modells besteht darin, dass es auf offenen Standards basiert und die Umsetzung des Modells oder der Client-Apps, die darauf zugreifen, nicht an eine bestimmte Implementierung bindet. Aus diesem Grund könnte ein REST-Webdienst mit Microsoft ASP.NET Core implementiert werden, und Client-Apps können mit beliebigen Sprachen und Toolsets entwickelt werden, die HTTP-Anforderungen generieren und HTTP-Antworten analysieren können.
Das REST-Modell verwendet ein Navigationsschema zur Darstellung von Objekten und Diensten (bezeichnet als „Ressourcen“) über ein Netzwerk. Systeme, die REST implementieren, verwenden in der Regel das HTTP-Protokoll zum Übertragen von Anforderungen für den Zugriff auf diese Ressourcen. In solchen Systemen sendet eine Client-App eine Anforderung in Form eines URI, der eine Ressource identifiziert, und einer HTTP-Methode (z. B. GET, POST, PUT oder DELETE), die angibt, welcher Vorgang für die Ressource ausgeführt werden soll. Der Text der HTTP-Anforderung enthält alle zum Ausführen des Vorgangs benötigten Daten.
Hinweis
REST definiert ein zustandsloses Anforderungsmodell. Daher müssen HTTP-Anforderungen unabhängig sein und können in beliebiger Reihenfolge erfolgen.
In der Antwort für eine REST-Anforderung werden standardmäßige HTTP-Statuscodes verwendet. Beispielsweise sollte eine Anforderung, die gültige Daten zurückgibt, den HTTP-Antwortcode 200 (OK
) enthalten, während eine Anforderung, die eine bestimmte Ressource nicht finden oder löschen kann, eine Antwort mit dem HTTP-Statuscode 404 (Not Found
) zurückgeben sollte.
Eine RESTful-Web-API macht eine Reihe verbundener Ressourcen verfügbar und stellt die grundlegenden Vorgänge bereit, die es einer Anwendung ermöglichen, diese Ressourcen zu manipulieren und einfach zwischen ihnen zu navigieren. Daher sind die URIs, die eine typische RESTful-Web-API bilden, auf die verfügbar gemachten Daten ausgerichtet und nutzen die von HTTP bereitgestellten Funktionen zur Arbeit mit diesen Daten.
Die von einer Client-App in eine HTTP-Anforderung eingefügten Daten und die zugehörigen Antwortnachrichten des Webservers können in einer Vielzahl von Formaten („Medientypen“) dargestellt werden. Wenn eine Client-App eine Anforderung sendet, die Daten im Text einer Nachricht zurückgibt, kann sie die von ihr verarbeitbaren Medientypen im Accept-Header der Anforderung angeben. Wenn der Webserver diesen Medientyp unterstützt, kann er eine Antwort mit einem Content-Type-Header zurückgeben, der das Format der Daten im Text der Nachricht angibt. Es liegt dann in der Verantwortung der Client-App, die Antwortnachricht zu analysieren und die Ergebnisse im Nachrichtentext entsprechend zu interpretieren.
Weitere Informationen zu REST finden Sie unter API-Entwurf und API-Implementierung in der Microsoft-Dokumentation.
Verwenden von RESTful-APIs
Die multiplattformfähige eShop-App verwendet das MVVM-Muster (Model View ViewModel), bei dem die Modellelemente des Musters die in der App verwendeten Domänenentitäten darstellen. Viele dieser Modellobjekte werden von den Controller- und Repositoryklassen in der eShop-Referenzanwendung akzeptiert und zurückgegeben. Daher werden sie als Datenübertragungsobjekte verwendet, die alle Daten enthalten, die zwischen der App und den containerisierten Microservices übergeben werden. Der Hauptvorteil der Verwendung von Datenübertragungsobjekten für die Übermittlung von Daten an den Dienst sowie den Empfang von Daten von einem Webdienst besteht darin, dass die App durch die Übertragung von mehr Daten in einem einzigen Remoteaufruf die Anzahl der erforderlichen Remoteaufrufe verringern kann.
Ausführen von Webanforderungen
Die multiplattformfähige eShop-App verwendet die HttpClient
-Klasse, um Anforderungen über HTTP zu senden, wobei JSON als Medientyp verwendet wird. Diese Klasse bietet die Funktionalität zum asynchronen Senden von HTTP-Anforderungen und zum Empfangen von HTTP-Antworten von einer per URI identifizierten Ressource. Die HttpResponseMessage-Klasse repräsentiert eine HTTP-Antwortnachricht, die von einer REST-API empfangen wird, nachdem eine HTTP-Anforderung gesendet wurde. Sie enthält Informationen zur Antwort, einschließlich Statuscode, Headern und beliebigem Text. Die HttpContent-Klasse repräsentiert den HTTP-Text und die Inhaltsheader dar, z. B. Content-Type und Content-Encoding. Der Inhalt kann je nach Format der Daten mit einer beliebigen der ReadAs
-Methoden (beispielsweise ReadAsStringAsync
und ReadAsByteArrayAsync
) gelesen werden.
Senden einer GET-Anforderung
Mithilfe der Klasse CatalogService
wird der Datenabruf aus dem Katalog-Microservice verwaltet. In der Methode RegisterViewModels
der MauiProgram
-Klasse wird die Klasse CatalogService
als Typzuordnung zum Typ ICatalogService
im Container für die Abhängigkeitsinjektion registriert. Wenn dann eine Instanz der CatalogViewModel
-Klasse erstellt wird, akzeptiert ihr Konstruktor einen ICatalogService type
, den der Container für die Abhängigkeitsinjektion auflöst und eine Instanz der CatalogService
-Klasse zurückgibt. Weitere Informationen zur Abhängigkeitsinjektion finden Sie unter Abhängigkeitsinjektion.
Die folgende Abbildung zeigt die Interaktion von Klassen, die Katalogdaten aus dem Katalog-Microservice lesen, um sie in „CatalogView“ anzuzeigen.
Beim Navigieren zu CatalogView
wird die Methode OnInitialize
in der Klasse „CatalogViewModel“ aufgerufen. Diese Methode ruft Katalogdaten aus dem Katalog-Microservice ab, wie im folgenden Codebeispiel gezeigt wird:
public override async Task InitializeAsync()
{
Products = await _productsService.GetCatalogAsync();
}
Diese Methode ruft die Methode GetCatalogAsync
der CatalogService
-Instanz auf, die vom Container für die Abhängigkeitsinjektion in CatalogViewModel
injiziert wurde. Die GetCatalogAsync
-Methode wird in folgendem Codebeispiel veranschaulicht:
public async Task<ObservableCollection<CatalogItem>> GetCatalogAsync()
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.CatalogEndpoint);
builder.Path = "api/v1/catalog/items";
string uri = builder.ToString();
CatalogRoot? catalog = await _requestProvider.GetAsync<CatalogRoot>(uri);
return catalog?.Data;
}
Diese Methode erstellt den URI zur Identifizierung der Ressource, an die die Anforderung gesendet wird, und verwendet die Klasse RequestProvider
, um die GET-HTTP-Methode für die Ressource aufzurufen, bevor die Ergebnisse an CatalogViewModel
zurückgegeben werden. Die Klasse RequestProvider
umfasst Funktionalität, um eine Anforderung in Form einer URI zur Identifizierung einer Ressource zu übermitteln, eine HTTP-Methode zum Angeben des Vorgangs, der für diese Ressource ausgeführt werden soll, und einen Textteil, der alle für die Ausführung des Vorgangs benötigten Daten enthält. Informationen dazu, wie die Klasse RequestProvider
in die Klasse CatalogService
injiziert wird, finden Sie unter Abhängigkeitsinjektion.
Das folgende Codebeispiel zeigt die Methode GetAsync
in der Klasse RequestProvider
:
public async Task<TResult> GetAsync<TResult>(string uri, string token = "")
{
HttpClient httpClient = GetOrCreateHttpClient(token);
HttpResponseMessage response = await httpClient.GetAsync(uri);
await HandleResponse(response);
TResult result = await response.Content.ReadFromJsonAsync<TResult>();
return result;
}
Diese Methode ruft die Methode GetOrCreateHttpClient
auf, die eine Instanz der Klasse HttpClient
mit den entsprechenden Headern zurückgibt. Anschließend wird eine asynchrone GET
-Anforderung an die per URI identifizierte Ressource übermittelt, wobei die Antwort in der HttpResponseMessage
-Instanz gespeichert wird. Dann wird die Methode HandleResponse
aufgerufen, die eine Ausnahme auslöst, wenn die Antwort keinen erfolgreichen HTTP-Statuscode enthält. Die Antwort wird als Zeichenfolge gelesen, von JSON in ein CatalogRoot
-Objekt konvertiert und an den CatalogService
zurückgegeben.
Die Methode GetOrCreateHttpClient
wird im folgenden Codebeispiel gezeigt:
private readonly Lazy<HttpClient> _httpClient =
new Lazy<HttpClient>(
() =>
{
var httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
return httpClient;
},
LazyThreadSafetyMode.ExecutionAndPublication);
private HttpClient GetOrCreateHttpClient(string token = "")
{
var httpClient = _httpClient.Value;
if (!string.IsNullOrEmpty(token))
{
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
}
else
{
httpClient.DefaultRequestHeaders.Authorization = null;
}
return httpClient;
}
Diese Methode erstellt eine neue Instanz oder ruft eine zwischengespeicherte Instanz der Klasse HttpClient
ab und legt den Accept-Header aller von der Instanz HttpClient
gesendeten Anforderungen auf application/json
fest. Damit wird angegeben, dass der Inhalt jeder Antwort mit JSON formatiert werden soll. Wenn ein Zugriffstoken als Argument an die GetOrCreateHttpClient
-Methode übergeben wurde, wird dieses in den Authorization
-Header aller von der HttpClient
-Instanz gesendeten Anforderungen eingefügt und mit der Zeichenfolge Bearer
als Präfix versehen. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.
Tipp
Es wird dringend empfohlen, Instanzen von HttpClient
zwischenzuspeichern und wiederzuverwenden, um die Anwendungsleistung zu verbessern. Die Erstellung eines neuen HttpClient
für jeden Vorgang kann zu Problemen aufgrund einer Socketerschöpfung führen. Weitere Informationen finden Sie unter HttpClient: Instanziierung im Microsoft Developer Center.
Wenn die GetAsync
-Methode in der RequestProvider
-Klasse HttpClient.GetAsync
aufruft, wird die Items
-Methode in der CatalogController
-Klasse im Catalog.API
-Projekt aufgerufen, wie im folgenden Codebeispiel gezeigt:
[HttpGet]
[Route("[action]")]
public async Task<IActionResult> Items(
[FromQuery]int pageSize = 10, [FromQuery]int pageIndex = 0)
{
var totalItems = await _catalogContext.CatalogItems
.LongCountAsync();
var itemsOnPage = await _catalogContext.CatalogItems
.OrderBy(c => c.Name)
.Skip(pageSize * pageIndex)
.Take(pageSize)
.ToListAsync();
itemsOnPage = ComposePicUri(itemsOnPage);
var model = new PaginatedItemsViewModel<CatalogItem>(
pageIndex, pageSize, totalItems, itemsOnPage);
return Ok(model);
}
Diese Methode ruft die Katalogdaten mit EntityFramework aus der SQL-Datenbank ab und gibt sie als Antwortnachricht zurück, die einen erfolgreichen HTTP-Statuscode und eine Sammlung von JSON-formatierten CatalogItem
Instanzen enthält.
Senden einer POST-Anforderung
Die Klasse BasketService
wird verwendet, um den Datenabruf und den Aktualisierungsprozess mit dem Microservice „Basket“ zu verwalten. In der Methode RegisterAppServices
der MauiProgram
-Klasse wird die Klasse BasketService
als Typzuordnung zum Typ IBasketService
im Container für die Abhängigkeitsinjektion registriert. Wenn dann eine Instanz der BasketViewModel
-Klasse erstellt wird, akzeptiert ihr Konstruktor einen IBasketService
-Typ, den der Container für die Abhängigkeitsinjektion auflöst und eine Instanz der BasketService
-Klasse zurückgibt. Weitere Informationen zur Abhängigkeitsinjektion finden Sie unter Abhängigkeitsinjektion.
Die Abbildung unten zeigt die Interaktion der Klassen, die die über „BasketView“ angezeigten Warenkorbdaten an den Microservice „Basket“ senden.
Wenn dem Warenkorb ein Element hinzugefügt wird, wird die ReCalculateTotalAsync
-Methode in der BasketViewModel
-Klasse aufgerufen. Diese Methode aktualisiert den Gesamtwert der Artikel im Warenkorb und sendet die Warenkorbdaten an den Microservice „Basket“, wie im folgenden Codebeispiel gezeigt:
private async Task ReCalculateTotalAsync()
{
// Omitted for brevity...
await _basketService.UpdateBasketAsync(
new CustomerBasket
{
BuyerId = userInfo.UserId,
Items = BasketItems.ToList()
},
authToken);
}
Diese Methode ruft die Methode UpdateBasketAsync
der BasketService
-Instanz auf, die vom Container für die Abhängigkeitsinjektion in BasketViewModel
injiziert wurde. Der folgende Code zeigt die UpdateBasketAsync
-Methode:
public async Task<CustomerBasket> UpdateBasketAsync(
CustomerBasket customerBasket, string token)
{
UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);
string uri = builder.ToString();
var result = await _requestProvider.PostAsync(uri, customerBasket, token);
return result;
}
Diese Methode erstellt den URI zur Identifizierung der Ressource, an die die Anforderung gesendet wird, und verwendet die Klasse RequestProvider
, um die POST-HTTP-Methode für die Ressource aufzurufen, bevor die Ergebnisse an BasketViewModel
zurückgegeben werden. Beachten Sie, dass für die Autorisierung von Anforderungen an den Microservice „Basket“ ein Zugriffstoken benötigt wird, das Sie während des Authentifizierungsprozesses vom IdentityServer
erhalten. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.
Das folgende Codebeispiel zeigt eine der PostAsync
-Methoden in der RequestProvider
-Klasse:
public async Task<TResult> PostAsync<TResult>(
string uri, TResult data, string token = "", string header = "")
{
HttpClient httpClient = GetOrCreateHttpClient(token);
var content = new StringContent(JsonSerializer.Serialize(data));
content.Headers.ContentType = new MediaTypeHeaderValue("application/json");
HttpResponseMessage response = await httpClient.PostAsync(uri, content);
await HandleResponse(response);
TResult result = await response.Content.ReadFromJsonAsync<TResult>();
return result;
}
Diese Methode ruft die Methode GetOrCreateHttpClient
auf, die eine Instanz der Klasse HttpClient
mit den entsprechenden Headern zurückgibt. Anschließend wird eine asynchrone POST-Anforderung an die durch den URI identifizierte Ressource gesendet, wobei die serialisierten Warenkorbdaten im JSON-Format übermittelt werden und die Antwort in der HttpResponseMessage
-Instanz gespeichert wird. Dann wird die Methode HandleResponse
aufgerufen, die eine Ausnahme auslöst, wenn die Antwort keinen erfolgreichen HTTP-Statuscode enthält. Die Antwort wird als Zeichenfolge gelesen, von JSON in ein CustomerBasket
-Objekt konvertiert und an „BasketService“ zurückgegeben. Weitere Informationen zur GetOrCreateHttpClient
-Methode finden Sie unter Erstellen einer GET-Anforderung.
Wenn die PostAsync
-Methode in der RequestProvider
-Klasse HttpClient.PostAsync
aufruft, wird die Post
-Methode in der BasketController
-Klasse im Basket.API
-Projekt aufgerufen, wie im folgenden Codebeispiel gezeigt:
[HttpPost]
public async Task<IActionResult> Post([FromBody] CustomerBasket value)
{
var basket = await _repository.UpdateBasketAsync(value);
return Ok(basket);
}
Diese Methode verwendet eine Instanz der Klasse RedisBasketRepository
, um die Warenkorbdaten im Redis-Cache zu speichern. Anschließend gibt sie eine Antwortnachricht zurück, die einen erfolgreichen HTTP-Statuscode und eine JSON-formatierte CustomerBasket
-Instanz enthält.
Senden einer DELETE-Anforderung
Die folgende Abbildung zeigt die Interaktionen der Klassen, die Warenkorbdaten aus dem Microservice „Basket“ löschen, für die CheckoutView
.
Beim Aufruf des Bezahlvorgangs wird die Methode CheckoutAsync
in der Klasse CheckoutViewModel
aufgerufen. Diese Methode erstellt eine neue Bestellung, bevor der Warenkorb gelöscht wird, wie im folgenden Codebeispiel gezeigt:
private async Task CheckoutAsync()
{
// Omitted for brevity...
await _basketService.ClearBasketAsync(
_shippingAddress.Id.ToString(), authToken);
}
Diese Methode ruft die Methode ClearBasketAsync
der BasketService
-Instanz auf, die vom Container für die Abhängigkeitsinjektion in CheckoutViewModel
injiziert wurde. Der folgende Code zeigt die ClearBasketAsync
-Methode:
public async Task ClearBasketAsync(string guidUser, string token)
{
UriBuilder builder = new(GlobalSetting.Instance.BasketEndpoint);
builder.Path = guidUser;
string uri = builder.ToString();
await _requestProvider.DeleteAsync(uri, token);
}
Diese Methode erstellt den URI zur Identifizierung der Ressource, an die die Anforderung gesendet wird, und verwendet die Klasse RequestProvider
, um die DELETE
-HTTP-Methode für die Ressource aufzurufen. Beachten Sie, dass für die Autorisierung von Anforderungen an den Microservice „Basket“ ein Zugriffstoken benötigt wird, das Sie während des Authentifizierungsprozesses vom IdentityServer
erhalten. Weitere Informationen zur Autorisierung finden Sie unter Autorisierung.
Das folgende Codebeispiel zeigt die Methode DeleteAsync
in der Klasse RequestProvider
:
public async Task DeleteAsync(string uri, string token = "")
{
HttpClient httpClient = GetOrCreateHttpClient(token);
await httpClient.DeleteAsync(uri);
}
Diese Methode ruft die Methode GetOrCreateHttpClient
auf, die eine Instanz der Klasse HttpClient
mit den entsprechenden Headern zurückgibt. Anschließend wird eine asynchrone DELETE
-Anforderung an die per URI identifizierte Ressource gesendet. Weitere Informationen zur GetOrCreateHttpClient
-Methode finden Sie unter Erstellen einer GET-Anforderung.
Wenn die DeleteAsync
-Methode in der RequestProvider
-Klasse HttpClient.DeleteAsync
aufruft, wird die Delete
-Methode in der BasketController
-Klasse im Basket.API
-Projekt aufgerufen, wie im folgenden Codebeispiel gezeigt:
[HttpDelete("{id}")]
public void Delete(string id) =>
_repository.DeleteBasketAsync(id);
Diese Methode verwendet eine Instanz der RedisBasketRepository
-Klasse, um die Warenkorbdaten aus dem Redis-Cache zu löschen.
Zwischenspeichern von Daten
Die Leistung einer App kann verbessert werden, indem häufig aufgerufene Daten in einem schnellem Speicher in der Nähe der App zwischengespeichert werden. Wenn sich dieser schnelle Datenspeicher näher an der App befindet als die ursprüngliche Datenquelle, kann das Zwischenspeichern die Antwortzeiten beim Abrufen von Daten erheblich verbessern.
Die gebräuchlichste Form der Zwischenspeicherung ist das Read-Through-Caching, bei dem eine App Daten durch Referenzierung des Caches abruft. Wenn die Daten nicht im Cache enthalten sind, werden sie aus dem Datenspeicher abgerufen und dem Cache hinzugefügt. Apps können das Read-Through-Caching mit dem cachefremden Programmierschema implementieren. Dieses Muster bestimmt, ob sich das Element derzeit im Cache befindet. Wenn das Element nicht im Cache enthalten ist, wird es aus dem Datenspeicher gelesen und dem Cache hinzugefügt. Weitere Informationen finden Sie unter Cachefremdes Muster in der Microsoft-Dokumentation.
Tipp
Speichern Sie Daten zwischen, die häufig gelesen werden und sich nur selten ändern.
Diese Daten können dem Cache bei Bedarf hinzugefügt werden, wenn sie zum ersten Mal von einer App abgerufen werden. Dadurch muss die App die Daten nur ein einziges Mal aus dem Datenspeicher abrufen, und spätere Zugriffe können über den Cache abgewickelt werden.
Verteilte Anwendungen, wie z. B. die Referenzanwendung eShop, sollten mindestens einen der beiden folgenden Caches bereitstellen:
- Ein gemeinsam genutzter Cache, auf den mehrere Prozesse oder Computer zugreifen können.
- Ein privater Cache, bei dem die Daten lokal auf dem Gerät gespeichert werden, auf dem die App ausgeführt wird.
Die multiplattformfähige eShop-App verwendet einen privaten Cache, bei dem Daten lokal auf dem Gerät, auf dem eine Instanz der App ausgeführt wird, gespeichert werden.
Tipp
Betrachten Sie den Cache als einen flüchtigen Datenspeicher, der jederzeit entfernt werden könnte.
Stellen Sie sicher, dass die Daten sowohl im ursprünglichen Datenspeicher als auch im Cache verwaltet werden. Dadurch wird das Risiko eines Datenverlusts minimiert, falls der Cache nicht mehr zugänglich ist.
Verwalten des Datenablaufs
Es sollte nicht erwartet werden, dass zwischengespeicherte Daten immer mit den Originaldaten übereinstimmen. Die Daten im ursprünglichen Datenspeicher können sich nach dem Zwischenspeichern ändern, wodurch die Daten im Cache veralten. Daher sollten Apps eine Strategie implementieren, mit der sichergestellt wird, dass die Daten im Cache möglichst aktuell sind. Gleichzeitig sollten sie jedoch auch Situationen erkennen und handhaben können, in denen die Daten im Cache veraltet sind. Die meisten Mechanismen für die Zwischenspeicherung bieten die Möglichkeit, für die Daten im Cache ein Ablaufdatum festzulegen. Dadurch wird der Zeitraum verkürzt, in dem Daten veralten können.
Tipp
Legen Sie beim Konfigurieren eines Caches eine Standardablaufzeit fest.
Viele Caches implementieren einen Ablauf, mit dem die Daten ungültig gemacht und aus dem Cache entfernt werden, wenn für einen angegebenen Zeitraum nicht darauf zugegriffen wurde. Bei der Auswahl des Ablaufzeitraums ist jedoch Vorsicht geboten. Bei einem zu kurzen Zeitraum laufen die Daten zu schnell ab, und die Vorteile der Zwischenspeicherung werden verringert. Werden die Daten zu lange aufbewahrt, besteht die Gefahr, dass sie veraltet sind. Daher sollte die Ablaufzeit dem Zugriffsmuster der Apps entsprechen, die die Daten nutzen.
Wenn zwischengespeicherte Daten ablaufen, sollten sie aus dem Cache entfernt werden. Die App muss die Daten dann aus dem ursprünglichen Datenspeicher abrufen und wieder in den Cache einfügen.
Wenn die Zeitspanne bis zum Ablauf der Daten zu lang ist, kann der Cache auch überlaufen. In diesem Fall müssen bei jeder Anforderung zum Hinzufügen neuer Elemente einige ältere Elemente aus dem Cache entfernt werden. Bei Cachediensten werden Daten in der Regel nach dem Prinzip der geringsten Nutzung entfernt. Es gibt jedoch auch andere Entfernungsrichtlinien, darunter zum Beispiel MRO (Most Recently Used) und FIFO (First In, First Out). Weitere Informationen finden Sie unter Anleitungen zum Caching in der Microsoft-Dokumentation.
Zwischenspeichern von Bildern
Die multiplattformfähige eShop-App nutzt remote gespeicherte Produktbilder, für die eine Zwischenspeicherung von Vorteil ist. Diese Bilder werden über das Image-Steuerelement angezeigt. Das Image-Steuerelement von .NET MAUI unterstützt die Zwischenspeicherung von heruntergeladenen Bildern, für die die Zwischenspeicherung standardmäßig aktiviert ist, und speichert das Bild lokal für 24 Stunden. Darüber hinaus kann die Ablaufzeit mit der Eigenschaft „CacheValidity“ konfiguriert werden. Weitere Informationen finden Sie unter Zwischenspeichern von Bildern im Microsoft Developer Center.
Erhöhen der Resilienz
Alle Apps, die mit Remotediensten und -ressourcen kommunizieren, müssen auf vorübergehende Störungen reagieren können. Zu diesen vorübergehende Störungen gehören der temporäre Verlust der Netzwerkverbindung mit Diensten, die vorübergehende Nichtverfügbarkeit eines Diensts oder Timeouts, die auftreten, wenn ein Dienst ausgelastet ist. Diese Fehler werden oft automatisch behoben, und wenn der Vorgang nach einer angemessenen Zeitspanne wiederholt wird, kann er wahrscheinlich erfolgreich ausgeführt werden.
Vorübergehende Fehler können eine enorme Auswirkung auf die wahrgenommene Qualität einer App haben, selbst wenn diese unter allen vorhersehbaren Umständen gründlich getestet wurde. Um die zuverlässige Funktion einer App zu gewährleisten, die mit Remotediensten kommuniziert, muss sie alle folgenden Aufgaben erfüllen können:
- Erkennung von Störungen bei deren Auftreten, und Ermittlung, ob es sich wahrscheinlich um vorübergehende Fehler handelt.
- Wiederholung des Vorgangs, wenn ein vorübergehender Fehler vermutet wird, und Aufzeichnung der Anzahl der Wiederholungsversuche für den Vorgang.
- Anwendung einer geeigneten Wiederholungsstrategie, mit der die Anzahl der Wiederholungsversuche, die Verzögerung zwischen den einzelnen Versuchen und die nach einem fehlgeschlagenen Versuch zu ergreifenden Maßnahmen festgelegt werden.
Diese Verarbeitung vorübergehender Fehler kann erreicht werden, indem alle Versuche zum Zugriff auf einen Remotedienst mit Code umschlossen werden, der das Wiederholungsmuster implementiert.
Wiederholungsmuster
Wenn eine App beim Senden einer Anforderung an einen Remotedienst einen Fehler feststellt, kann sie diesen Fehler auf eine der folgenden Arten behandeln:
- Wiederholung des Vorgangs: Die App führt die fehlgeschlagene Anforderung sofort erneut aus.
- Wiederholung des Vorgangs nach einer Verzögerung: Die App wartet einen angemessenen Zeitraum, bevor sie die Anforderung wiederholt.
- Abbruch des Vorgangs: Die App bricht den Vorgang ab und meldet eine Ausnahme.
Die Wiederholungsstrategie sollte auf die geschäftlichen Anforderungen der App abgestimmt sein. Beispielsweise ist es wichtig, die Anzahl der Wiederholungsversuche und das Wiederholungsintervall auf den jeweiligen Vorgang abzustimmen. Wenn der Vorgang Teil einer Benutzerinteraktion ist, sollte das Wiederholungsintervall kurz sein, und es sollten nur wenige Wiederholungsversuche unternommen werden, damit die Benutzer nicht auf eine Antwort warten müssen. Wenn der Vorgang Teil eines zeitintensiven Workflows ist, bei dem ein Abbruch oder Neustart des Workflows kostspielig oder zeitaufwändig ist, ist es sinnvoll, zwischen den Versuchen länger zu warten und mehrere Wiederholungsversuche durchzuführen.
Hinweis
Eine aggressive Wiederholungsstrategie mit minimaler Verzögerung zwischen den Versuchen und einer großen Anzahl von Wiederholungsversuchen kann einen Remotedienst, der seine Kapazitätsgrenze nahezu oder ganz erreicht hat, zusätzlich beeinträchtigen. Darüber hinaus kann sich eine solche Wiederholungsstrategie auch auf die Reaktionsfähigkeit der App auswirken, wenn ständig versucht wird, einen fehlgeschlagenen Vorgang auszuführen.
Wenn eine Anforderung nach mehreren Wiederholungsversuchen weiterhin fehlschlägt, ist es für die App besser, weitere Anforderungen an dieselbe Ressource zu verhindern und einen Fehler zu melden. Nach Ablauf einer bestimmten Zeitspanne kann die App dann eine oder mehrere Anforderungen an die Ressource senden, um zu ermitteln, ob diese erfolgreich durchgeführt werden können. Weitere Informationen finden Sie unter Muster „Trennschalter“.
Tipp
Implementieren Sie niemals einen endlosen Wiederholungsmechanismus. Bevorzugen Sie stattdessen ein exponentielles Backoff.
Verwenden Sie eine begrenzte Anzahl von Wiederholungsversuchen, oder implementieren Sie das Muster Trennschalter, damit der Dienst wiederhergestellt werden kann.
Die eShop-Referenzanwendung implementiert das Wiederholungsmuster.
Weitere Informationen zum Wiederholungsmuster finden Sie unter Wiederholungsmuster in der Microsoft-Dokumentation.
Muster „Trennschalter“
In bestimmten Situationen können Fehler aufgrund von vorhersehbaren Ereignissen auftreten, deren Behebung länger dauert. Diese Fehler reichen von einem teilweisen Verlust der Konnektivität bis hin zum vollständigen Ausfall eines Diensts. In solchen Situationen hat es keinen Sinn, dass eine App einen Vorgang wiederholt, der wahrscheinlich nicht erfolgreich ausgeführt werden kann. Stattdessen sollte die App akzeptieren, dass der Vorgang fehlgeschlagen ist und diesen Fehler entsprechend behandeln.
Das Trennschaltermuster kann verhindern, dass eine App wiederholt versucht, einen Vorgang auszuführen, der wahrscheinlich fehlschlägt. Gleichzeitig kann die App erkennen, ob der Fehler behoben wurde.
Hinweis
Der Zweck des Trennschaltermuster unterscheidet sich vom Wiederholungsmuster. Mithilfe des Wiederholungsmusters kann eine App einen Vorgang in der Annahme wiederholen, dass dieser erfolgreich ausgeführt wird. Das Trennschaltermuster hindert eine App daran, einen Vorgang auszuführen, bei dem es wahrscheinlich zu einem Fehler kommt.
Ein Trennschalter fungiert als Proxy für Vorgänge, bei denen möglicherweise Fehler auftreten. Der Proxy sollte die Anzahl der zuletzt aufgetretenen Fehler überwachen und anhand dieser Informationen entscheiden, ob der Vorgang fortgesetzt werden darf, oder ob sofort eine Ausnahme zurückgegeben werden soll.
Die multiplattformfähige eShop-App implementiert das Trennschaltermuster derzeit nicht, Allerdings tut dies eShop.
Tipp
Kombinieren Sie das Wiederholungs- und Trennschaltermuster.
Eine Anwendung kann beide Muster kombinieren, indem sie das Wiederholungsmuster nutzt, um einen Vorgang über das Trennschaltermuster aufzurufen. Die Wiederholungslogik sollte jedoch auf Ausnahmen reagieren, die vom Trennschalter zurückgegeben werden, und Wiederholungsversuche abbrechen, wenn der Trennschalter anzeigt, dass ein Fehler nicht vorübergehend ist.
Weitere Informationen zum Trennschaltermuster finden Sie im Artikel zum Trennschaltermuster in der Microsoft-Dokumentation.
Zusammenfassung
Zahlreiche moderne webbasierte Lösungen nutzen auf Webservern gehostete Webdienste, um Funktionen für Remoteclientanwendungen bereitzustellen. Die von einem Webdienst verfügbar gemachten Vorgänge definieren eine Web-API, und Client-Apps sollten die Web-API nutzen können, ohne Kenntnis darüber zu haben, wie die von der API offengelegten Daten oder Vorgänge implementiert sind.
Die Leistung einer App kann verbessert werden, indem häufig aufgerufene Daten in einem schnellem Speicher in der Nähe der App zwischengespeichert werden. Apps können das Read-Through-Caching mit dem cachefremden Programmierschema implementieren. Dieses Muster bestimmt, ob sich das Element derzeit im Cache befindet. Wenn das Element nicht im Cache enthalten ist, wird es aus dem Datenspeicher gelesen und dem Cache hinzugefügt.
Bei der Kommunikation mit Web-APIs müssen Apps auf vorübergehende Störungen reagieren können. Zu diesen vorübergehende Störungen gehören der temporäre Verlust der Netzwerkverbindung mit Diensten, die vorübergehende Nichtverfügbarkeit eines Diensts oder Timeouts, die auftreten, wenn ein Dienst ausgelastet ist. Diese Fehler werden oft automatisch behoben, und wenn der Vorgang nach einer angemessenen Zeitspanne wiederholt wird, kann er wahrscheinlich erfolgreich ausgeführt werden. Apps sollten daher alle Versuche zum Zugriff auf eine Web-API mit Code umschließen, der einen Mechanismus zur Behandlung vorübergehender Fehler implementiert.