Accesso ai dati remoti

Nota

Questo eBook è stato pubblicato nella primavera del 2017 e non è stato aggiornato da allora. C'è molto nel libro che rimane prezioso, ma alcuni dei materiali sono obsoleti.

Molte soluzioni moderne basate sul Web usano servizi Web, ospitati da server Web, per fornire funzionalità per le applicazioni client remote. Le operazioni esposte da un servizio Web costituiscono un'API Web.

Le app client devono essere in grado di usare l'API Web senza sapere come vengono implementati i dati o le operazioni esposte dall'API. Ciò richiede che l'API rispetti gli standard comuni che consentono a un'app client e a un servizio Web di accettare i formati di dati da usare e la struttura dei dati scambiati tra le app client e il servizio Web.

Introduzione al trasferimento di stato rappresentazione

Il trasferimento di stato rappresentazione (REST) è uno stile architettonico per la creazione di sistemi distribuiti basati su ipermedia. Un vantaggio principale del modello REST è che si basa su standard aperti e non associa l'implementazione del modello o delle app client che vi accedono a un'implementazione specifica. Pertanto, è possibile implementare un servizio Web REST usando Microsoft ASP.NET Core MVC e le app client possono essere sviluppate usando qualsiasi linguaggio e set di strumenti in grado di generare richieste HTTP e analizzare le risposte HTTP.

Il modello REST usa uno schema di navigazione per rappresentare oggetti e servizi in una rete, detti risorse. I sistemi che implementano REST usano in genere il protocollo HTTP per trasmettere le richieste per accedere a queste risorse. In questi sistemi, un'app client invia una richiesta sotto forma di URI che identifica una risorsa e un metodo HTTP (ad esempio GET, POST, PUT o DELETE) che indica l'operazione da eseguire su tale risorsa. Il corpo della richiesta HTTP contiene tutti i dati necessari per eseguire l'operazione.

Nota

REST definisce un modello di richiesta senza stato. Pertanto, le richieste HTTP devono essere indipendenti e possono verificarsi in qualsiasi ordine.

La risposta da una richiesta REST usa codici di stato HTTP standard. Ad esempio, una richiesta che restituisce dati validi deve includere il codice di risposta HTTP 200 (OK), mentre una richiesta che non riesce a trovare o eliminare una risorsa specificata deve restituire una risposta che include il codice di stato HTTP 404 (Non trovato).

Un'API Web RESTful espone un set di risorse connesse e fornisce le operazioni di base che consentono a un'app di modificare tali risorse e spostarsi facilmente tra di esse. Per questo motivo, gli URI che costituiscono una tipica API Web RESTful sono orientati ai dati esposti e usano le funzionalità fornite da HTTP per operare su questi dati.

I dati inclusi da un'app client in una richiesta HTTP e i messaggi di risposta corrispondenti dal server Web possono essere presentati in diversi formati, noti come tipi di supporti. Quando un'app client invia una richiesta che restituisce dati nel corpo di un messaggio, può specificare i tipi di supporto che può gestire nell'intestazione Accept della richiesta. Se il server Web supporta questo tipo di supporto, può rispondere con una risposta che include l'intestazione Content-Type che specifica il formato dei dati nel corpo del messaggio. È quindi responsabilità dell'app client analizzare il messaggio di risposta e interpretare i risultati nel corpo del messaggio in modo appropriato.

Per altre informazioni su REST, vedere Progettazione API e implementazione dell'API.

Uso delle API RESTful

L'app per dispositivi mobili eShopOnContainers usa il modello Model-View-ViewModel (MVVM) e gli elementi del modello del modello rappresentano le entità di dominio usate nell'app. Le classi controller e repository nell'applicazione di riferimento eShopOnContainers accettano e restituiscono molti di questi oggetti modello. Pertanto, vengono usati come oggetti di trasferimento dati (DTO) che contengono tutti i dati passati tra l'app per dispositivi mobili e i microservizi in contenitori. Il vantaggio principale dell'uso di DTO per passare e ricevere dati da un servizio Web è che trasmettendo più dati in una singola chiamata remota, l'app può ridurre il numero di chiamate remote che devono essere effettuate.

Esecuzione di richieste Web

L'app per dispositivi mobili eShopOnContainers usa la HttpClient classe per effettuare richieste tramite HTTP, con JSON usato come tipo di supporto. Questa classe fornisce funzionalità per l'invio asincrono di richieste HTTP e la ricezione di risposte HTTP da una risorsa identificata dall'URI. La HttpResponseMessage classe rappresenta un messaggio di risposta HTTP ricevuto da un'API REST dopo che è stata effettuata una richiesta HTTP. Contiene informazioni sulla risposta, inclusi il codice di stato, le intestazioni e qualsiasi corpo. La HttpContent classe rappresenta il corpo HTTP e le intestazioni del contenuto, ad esempio Content-Type e Content-Encoding. Il contenuto può essere letto usando uno dei ReadAs metodi, ad esempio ReadAsStringAsync e ReadAsByteArrayAsync, a seconda del formato dei dati.

Creazione di una richiesta GET

La CatalogService classe viene usata per gestire il processo di recupero dei dati dal microservizio del catalogo. RegisterDependencies Nel metodo nella ViewModelLocator classe la CatalogService classe viene registrata come mapping del tipo rispetto al ICatalogService tipo con il contenitore di inserimento delle dipendenze Autofac. Quindi, quando viene creata un'istanza della CatalogViewModel classe, il relativo costruttore accetta un ICatalogService tipo, risolto da Autofac, restituendo un'istanza della CatalogService classe . Per altre informazioni sull'inserimento delle dipendenze, vedere Introduzione all'inserimento delle dipendenze.

La figura 10-1 mostra l'interazione delle classi che leggono i dati del catalogo dal microservizio del catalogo per la visualizzazione da parte di CatalogView.

Retrieving data from the catalog microservice

Figura 10-1: Recupero di dati dal microservizio del catalogo

CatalogView Quando si passa a , viene chiamato il OnInitialize metodo nella CatalogViewModel classe . Questo metodo recupera i dati del catalogo dal microservizio catalogo, come illustrato nell'esempio di codice seguente:

public override async Task InitializeAsync(object navigationData)  
{  
    ...  
    Products = await _productsService.GetCatalogAsync();  
    ...  
}

Questo metodo chiama il GetCatalogAsync metodo dell'istanza CatalogService inserita in CatalogViewModel da Autofac. L'esempio di codice seguente illustra il metodo GetCatalogAsync:

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.ToObservableCollection();            
}

Questo metodo compila l'URI che identifica la risorsa a cui verrà inviata la richiesta e usa la RequestProvider classe per richiamare il metodo HTTP GET sulla risorsa, prima di restituire i risultati a CatalogViewModel. La RequestProvider classe contiene funzionalità che inviano una richiesta sotto forma di URI che identifica una risorsa, un metodo HTTP che indica l'operazione da eseguire su tale risorsa e un corpo che contiene i dati necessari per eseguire l'operazione. Per informazioni su come la RequestProvider classe viene inserita in CatalogService class, vedere Introduzione all'inserimento delle dipendenze.

Nell'esempio di codice seguente viene illustrato il GetAsync metodo nella RequestProvider classe :

public async Task<TResult> GetAsync<TResult>(string uri, string token = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    HttpResponseMessage response = await httpClient.GetAsync(uri);  

    await HandleResponse(response);  
    string serialized = await response.Content.ReadAsStringAsync();  

    TResult result = await Task.Run(() =>   
        JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));  

    return result;  
}

Questo metodo chiama il CreateHttpClient metodo , che restituisce un'istanza della HttpClient classe con le intestazioni appropriate impostate. Invia quindi una richiesta GET asincrona alla risorsa identificata dall'URI, con la risposta archiviata nell'istanza HttpResponseMessage . Viene quindi richiamato il HandleResponse metodo , che genera un'eccezione se la risposta non include un codice di stato HTTP riuscito. La risposta viene quindi letta come stringa, convertita da JSON a un CatalogRoot oggetto e restituita a CatalogService.

Il CreateHttpClient metodo è illustrato nell'esempio di codice seguente:

private HttpClient CreateHttpClient(string token = "")  
{  
    var httpClient = new HttpClient();  
    httpClient.DefaultRequestHeaders.Accept.Add(  
        new MediaTypeWithQualityHeaderValue("application/json"));  

    if (!string.IsNullOrEmpty(token))  
    {  
        httpClient.DefaultRequestHeaders.Authorization =   
            new AuthenticationHeaderValue("Bearer", token);  
    }  
    return httpClient;  
}

Questo metodo crea una nuova istanza della HttpClient classe e imposta l'intestazione Accept di tutte le richieste effettuate dall'istanza HttpClient su application/json, che indica che prevede che il contenuto di qualsiasi risposta venga formattato tramite JSON. Se quindi un token di accesso è stato passato come argomento al CreateHttpClient metodo , viene aggiunto all'intestazione Authorization di tutte le richieste effettuate dall'istanza HttpClient , preceduto dalla stringa Bearer. Per altre informazioni sull'autorizzazione, vedere Autorizzazione.

Quando il GetAsync metodo nella RequestProvider classe chiama HttpClient.GetAsync, viene richiamato il Items metodo nella CatalogController classe nel progetto Catalog.API, illustrato nell'esempio di codice seguente:

[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);  
}

Questo metodo recupera i dati del catalogo dal database SQL usando EntityFramework e lo restituisce come messaggio di risposta che include un codice di stato HTTP riuscito e una raccolta di istanze in CatalogItem formato JSON.

Creazione di una richiesta POST

La BasketService classe viene usata per gestire il processo di recupero e aggiornamento dei dati con il microservizio basket. RegisterDependencies Nel metodo nella ViewModelLocator classe la BasketService classe viene registrata come mapping del tipo rispetto al IBasketService tipo con il contenitore di inserimento delle dipendenze Autofac. Quindi, quando viene creata un'istanza della BasketViewModel classe, il relativo costruttore accetta un IBasketService tipo, risolto da Autofac, restituendo un'istanza della BasketService classe . Per altre informazioni sull'inserimento delle dipendenze, vedere Introduzione all'inserimento delle dipendenze.

La figura 10-2 mostra l'interazione delle classi che inviano i dati del carrello visualizzati da BasketView, al microservizio basket.

Sending data to the basket microservice

Figura 10-2: Invio di dati al microservizio basket

Quando un elemento viene aggiunto al carrello acquisti, viene chiamato il ReCalculateTotalAsync metodo nella BasketViewModel classe . Questo metodo aggiorna il valore totale degli elementi nel carrello e invia i dati del carrello al microservizio basket, come illustrato nell'esempio di codice seguente:

private async Task ReCalculateTotalAsync()  
{  
    ...  
    await _basketService.UpdateBasketAsync(new CustomerBasket  
    {  
        BuyerId = userInfo.UserId,   
        Items = BasketItems.ToList()  
    }, authToken);  
}

Questo metodo chiama il UpdateBasketAsync metodo dell'istanza BasketService inserita in BasketViewModel da Autofac. Il metodo seguente illustra il UpdateBasketAsync metodo :

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;  
}

Questo metodo compila l'URI che identifica la risorsa a cui verrà inviata la richiesta e usa la RequestProvider classe per richiamare il metodo HTTP POST sulla risorsa, prima di restituire i risultati a BasketViewModel. Si noti che un token di accesso, ottenuto da IdentityServer durante il processo di autenticazione, è necessario per autorizzare le richieste al microservizio basket. Per altre informazioni sull'autorizzazione, vedere Autorizzazione.

Nell'esempio di codice seguente viene illustrato uno dei PostAsync metodi nella RequestProvider classe :

public async Task<TResult> PostAsync<TResult>(  
    string uri, TResult data, string token = "", string header = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    ...  
    var content = new StringContent(JsonConvert.SerializeObject(data));  
    content.Headers.ContentType = new MediaTypeHeaderValue("application/json");  
    HttpResponseMessage response = await httpClient.PostAsync(uri, content);  

    await HandleResponse(response);  
    string serialized = await response.Content.ReadAsStringAsync();  

    TResult result = await Task.Run(() =>  
        JsonConvert.DeserializeObject<TResult>(serialized, _serializerSettings));  

    return result;  
}

Questo metodo chiama il CreateHttpClient metodo , che restituisce un'istanza della HttpClient classe con le intestazioni appropriate impostate. Invia quindi una richiesta POST asincrona alla risorsa identificata dall'URI, con i dati del carrello serializzati inviati in formato JSON e la risposta archiviata nell'istanza HttpResponseMessage . Viene quindi richiamato il HandleResponse metodo , che genera un'eccezione se la risposta non include un codice di stato HTTP riuscito. La risposta viene quindi letta come stringa, convertita da JSON a un CustomerBasket oggetto e restituita a BasketService. Per altre informazioni sul CreateHttpClient metodo, vedere Creazione di una richiesta GET.

Quando il PostAsync metodo nella RequestProvider classe chiama HttpClient.PostAsync, viene richiamato il Post metodo nella BasketController classe nel progetto Basket.API, illustrato nell'esempio di codice seguente:

[HttpPost]  
public async Task<IActionResult> Post([FromBody]CustomerBasket value)  
{  
    var basket = await _repository.UpdateBasketAsync(value);  
    return Ok(basket);  
}

Questo metodo usa un'istanza della RedisBasketRepository classe per rendere persistenti i dati del carrello nella cache Redis e lo restituisce come messaggio di risposta che include un codice di stato HTTP riuscito e un'istanza in CustomerBasket formato JSON.

Esecuzione di una richiesta DELETE

La figura 10-3 mostra le interazioni delle classi che eliminano i dati del carrello dal microservizio basket, per .CheckoutView

Deleteing data from the basket microservice

Figura 10-3: Eliminazione di dati dal microservizio basket

Quando viene richiamato il processo di estrazione, viene chiamato il CheckoutAsync metodo nella CheckoutViewModel classe . Questo metodo crea un nuovo ordine, prima di cancellare il carrello acquisti, come illustrato nell'esempio di codice seguente:

private async Task CheckoutAsync()  
{  
    ...  
    await _basketService.ClearBasketAsync(_shippingAddress.Id.ToString(), authToken);  
    ...  
}

Questo metodo chiama il ClearBasketAsync metodo dell'istanza BasketService inserita in CheckoutViewModel da Autofac. Il metodo seguente illustra il ClearBasketAsync metodo :

public async Task ClearBasketAsync(string guidUser, string token)  
{  
    UriBuilder builder = new UriBuilder(GlobalSetting.Instance.BasketEndpoint);  
    builder.Path = guidUser;  
    string uri = builder.ToString();  
    await _requestProvider.DeleteAsync(uri, token);  
}

Questo metodo compila l'URI che identifica la risorsa a cui verrà inviata la richiesta e usa la RequestProvider classe per richiamare il metodo HTTP DELETE sulla risorsa. Si noti che un token di accesso, ottenuto da IdentityServer durante il processo di autenticazione, è necessario per autorizzare le richieste al microservizio basket. Per altre informazioni sull'autorizzazione, vedere Autorizzazione.

Nell'esempio di codice seguente viene illustrato il DeleteAsync metodo nella RequestProvider classe :

public async Task DeleteAsync(string uri, string token = "")  
{  
    HttpClient httpClient = CreateHttpClient(token);  
    await httpClient.DeleteAsync(uri);  
}

Questo metodo chiama il CreateHttpClient metodo , che restituisce un'istanza della HttpClient classe con le intestazioni appropriate impostate. Invia quindi una richiesta DELETE asincrona alla risorsa identificata dall'URI. Per altre informazioni sul CreateHttpClient metodo, vedere Creazione di una richiesta GET.

Quando il DeleteAsync metodo nella RequestProvider classe chiama HttpClient.DeleteAsync, viene richiamato il Delete metodo nella BasketController classe nel progetto Basket.API, illustrato nell'esempio di codice seguente:

[HttpDelete("{id}")]  
public void Delete(string id)  
{  
    _repository.DeleteBasketAsync(id);  
}

Questo metodo usa un'istanza della RedisBasketRepository classe per eliminare i dati del carrello dalla cache Redis.

Memorizzazione di dati nella cache

Le prestazioni di un'app possono essere migliorate memorizzando nella cache i dati a cui si accede di frequente per l'archiviazione veloce che si trova vicino all'app. Se lo spazio di archiviazione veloce si trova più vicino all'app rispetto all'origine originale, la memorizzazione nella cache può migliorare significativamente i tempi di risposta durante il recupero dei dati.

La forma più comune di memorizzazione nella cache è la memorizzazione nella cache di lettura-through, in cui un'app recupera i dati facendo riferimento alla cache. Se non sono presenti nella cache, i dati vengono recuperati dall'archivio dati e aggiunti alla cache. Le app possono implementare la memorizzazione nella cache in lettura con il modello cache-aside. Questo modello determina se l'elemento è attualmente presente nella cache. Se l'elemento non è presente nella cache, viene letto dall'archivio dati e aggiunto alla cache. Per altre informazioni, vedere il modello Cache-Aside .

Suggerimento

Memorizzare nella cache i dati letti di frequente e che cambiano raramente. Questi dati possono essere aggiunti alla cache su richiesta la prima volta che vengono recuperati da un'app. Ciò significa che l'app deve recuperare i dati una sola volta dall'archivio dati e che l'accesso successivo può essere soddisfatto usando la cache.

Le applicazioni distribuite, ad esempio l'applicazione di riferimento eShopOnContainers, devono fornire o entrambe le cache seguenti:

  • Una cache condivisa, accessibile da più processi o computer.
  • Una cache privata, in cui i dati sono contenuti localmente nel dispositivo che esegue l'app.

L'app per dispositivi mobili eShopOnContainers usa una cache privata, in cui i dati vengono mantenuti localmente nel dispositivo che esegue un'istanza dell'app. Per informazioni sulla cache usata dall'applicazione di riferimento eShopOnContainers, vedere Microservizi .NET: Architettura per applicazioni .NET in contenitori.

Suggerimento

Si consideri la cache come un archivio dati temporaneo che potrebbe scomparire in qualsiasi momento. Assicurarsi che i dati vengano mantenuti nell'archivio dati originale e nella cache. Le probabilità di perdita dei dati vengono quindi ridotte al minimo se la cache non è più disponibile.

Gestione della scadenza dei dati

È poco pratico aspettarsi che i dati memorizzati nella cache siano sempre coerenti con i dati originali. I dati nell'archivio dati originale potrebbero cambiare dopo essere stati memorizzati nella cache, causando la mancata conservazione dei dati memorizzati nella cache. Di conseguenza, le app devono implementare una strategia che consenta di garantire che i dati nella cache siano il più aggiornati possibile, ma possono anche rilevare e gestire situazioni che si verificano quando i dati nella cache sono diventati obsoleti. La maggior parte dei meccanismi di memorizzazione nella cache consente di configurare la cache per la scadenza dei dati e quindi ridurre il periodo per cui i dati potrebbero non essere aggiornati.

Suggerimento

Impostare un'ora di scadenza predefinita durante la configurazione di una cache. Molte cache implementano la scadenza, che invalida i dati e la rimuove dalla cache se non è accessibile per un periodo specificato. Tuttavia, è necessario prestare attenzione quando si sceglie il periodo di scadenza. Se è troppo breve, i dati scadono troppo rapidamente e i vantaggi della memorizzazione nella cache verranno ridotti. Se è troppo lungo, i dati rischiano di diventare obsoleti. Pertanto, l'ora di scadenza deve corrispondere al modello di accesso per le app che usano i dati.

Quando i dati memorizzati nella cache scadono, devono essere rimossi dalla cache e l'app deve recuperare i dati dall'archivio dati originale e inserirli nella cache.

È anche possibile che una cache venga riempita se i dati possono rimanere per un periodo troppo lungo. Pertanto, le richieste di aggiunta di nuovi elementi alla cache potrebbero essere necessarie per rimuovere alcuni elementi in un processo noto come rimozione. I servizi di memorizzazione nella cache in genere eliminano i dati in modo meno recente. Esistono tuttavia altri criteri di rimozione, tra cui quelli usati più di recente e first-in-first-out. Per altre informazioni, vedere Indicazioni sulla memorizzazione nella cache.

Memorizzazione nella cache delle immagini

L'app per dispositivi mobili eShopOnContainers usa immagini di prodotti remoti che traggono vantaggio dalla memorizzazione nella cache. Queste immagini vengono visualizzate dal Image controllo e dal CachedImage controllo fornito dalla libreria FFImageLoading .

Il Xamarin.FormsImage controllo supporta la memorizzazione nella cache delle immagini scaricate. La memorizzazione nella cache è abilitata per impostazione predefinita e archivierà l'immagine in locale per 24 ore. Inoltre, l'ora di scadenza può essere configurata con la CacheValidity proprietà . Per altre informazioni, vedere Downloaded Image Caching.For more information, see Downloaded Image Caching.

Il controllo FFImageLoading CachedImage è una sostituzione del Xamarin.FormsImage controllo, fornendo proprietà aggiuntive che consentono funzionalità supplementari. Tra queste funzionalità, il controllo fornisce la memorizzazione nella cache configurabile, supportando al tempo stesso i segnaposto dell'immagine di errore e caricamento. L'esempio di codice seguente illustra come l'app per dispositivi mobili eShopOnContainers usa il CachedImage controllo in ProductTemplate, ovvero il modello di dati usato dal ListView controllo in CatalogView:

<ffimageloading:CachedImage
    Grid.Row="0"
    Source="{Binding PictureUri}"     
    Aspect="AspectFill">
    <ffimageloading:CachedImage.LoadingPlaceholder>
        <OnPlatform x:TypeArguments="ImageSource">
            <On Platform="iOS, Android" Value="default_campaign" />
            <On Platform="UWP" Value="Assets/default_campaign.png" />
        </OnPlatform>
    </ffimageloading:CachedImage.LoadingPlaceholder>
    <ffimageloading:CachedImage.ErrorPlaceholder>
        <OnPlatform x:TypeArguments="ImageSource">
            <On Platform="iOS, Android" Value="noimage" />
            <On Platform="UWP" Value="Assets/noimage.png" />
        </OnPlatform>
    </ffimageloading:CachedImage.ErrorPlaceholder>
</ffimageloading:CachedImage>

Il CachedImage controllo imposta le LoadingPlaceholder proprietà e ErrorPlaceholder sulle immagini specifiche della piattaforma. La LoadingPlaceholder proprietà specifica l'immagine da visualizzare mentre viene recuperata l'immagine Source specificata dalla proprietà e la ErrorPlaceholder proprietà specifica l'immagine da visualizzare se si verifica un errore quando si tenta di recuperare l'immagine specificata dalla Source proprietà .

Come suggerisce il nome, il CachedImage controllo memorizza nella cache le immagini remote nel dispositivo per l'ora specificata dal valore della CacheDuration proprietà. Quando questo valore della proprietà non viene impostato in modo esplicito, viene applicato il valore predefinito di 30 giorni.

Aumento della resilienza

Tutte le app che comunicano con servizi remoti e risorse devono essere sensibili agli errori temporanei. Gli errori temporanei includono la perdita momentanea della connettività di rete ai servizi, l'indisponibilità temporanea di un servizio o i timeout che si verificano quando un servizio è occupato. Questi errori sono spesso autocorribili e, se l'azione viene ripetuta dopo un ritardo appropriato, è probabile che abbia esito positivo.

Gli errori temporanei possono avere un impatto enorme sulla qualità percepita di un'app, anche se è stato testato accuratamente in tutte le circostanze prevedibili. Per garantire che un'app che comunica con i servizi remoti funzioni in modo affidabile, deve essere in grado di eseguire tutte le operazioni seguenti:

  • Rilevare gli errori quando si verificano e determinare se è probabile che gli errori siano temporanei.
  • Ripetere l'operazione se determina che è probabile che l'errore sia temporaneo e tenere traccia del numero di tentativi di ripetizione dell'operazione.
  • Usare una strategia di ripetizione dei tentativi appropriata, che specifica il numero di tentativi, il ritardo tra ogni tentativo e le azioni da eseguire dopo un tentativo non riuscito.

Questa gestione degli errori temporanei può essere ottenuta eseguendo il wrapping di tutti i tentativi di accesso a un servizio remoto nel codice che implementa il modello di ripetizione dei tentativi.

Modello di ripetizione dei tentativi

Se un'app rileva un errore quando tenta di inviare una richiesta a un servizio remoto, può gestire l'errore in uno dei modi seguenti:

  • Ripetere l'operazione. L'app potrebbe ritentare immediatamente la richiesta non riuscita.
  • Ripetere l'operazione dopo un ritardo. L'app deve attendere un periodo di tempo appropriato prima di ripetere la richiesta.
  • Annullamento dell'operazione. L'applicazione deve annullare l'operazione e segnalare un'eccezione.

La strategia di ripetizione dei tentativi deve essere ottimizzata per soddisfare i requisiti aziendali dell'app. Ad esempio, è importante ottimizzare il numero di tentativi e l'intervallo di ripetizione dei tentativi per l'operazione tentata. Se l'operazione fa parte di un'interazione dell'utente, l'intervallo di ripetizione dei tentativi deve essere breve e solo alcuni tentativi tentati di evitare di fare in modo che gli utenti attendino una risposta. Se l'operazione fa parte di un flusso di lavoro a esecuzione prolungata, in cui l'annullamento o il riavvio del flusso di lavoro è costoso o richiede molto tempo, è opportuno attendere più tempo tra i tentativi e riprovare più volte.

Nota

Una strategia di ripetizione aggressiva con un ritardo minimo tra i tentativi e un numero elevato di tentativi potrebbe compromettere un servizio remoto in esecuzione vicino o alla capacità. Inoltre, tale strategia di ripetizione dei tentativi potrebbe anche influire sulla velocità di risposta dell'app se tenta continuamente di eseguire un'operazione non riuscita.

Se una richiesta ha ancora esito negativo dopo un certo numero di tentativi, è preferibile che l'app impedisca ulteriori richieste che passano alla stessa risorsa e di segnalare un errore. Quindi, dopo un periodo impostato, l'app può effettuare una o più richieste alla risorsa per verificare se hanno esito positivo. Per altre informazioni, vedere Circuit Breaker Pattern (Modello a interruttore).

Suggerimento

Non implementare mai un meccanismo a ciclo infinito. Usare un numero finito di tentativi o implementare il modello Interruttore per consentire il ripristino di un servizio.

L'app per dispositivi mobili eShopOnContainers attualmente non implementa il modello di ripetizione dei tentativi durante l'esecuzione di richieste Web RESTful. Tuttavia, il CachedImage controllo, fornito dalla libreria FFImageLoading supporta la gestione degli errori temporanei ritentando il caricamento delle immagini. Se il caricamento delle immagini non riesce, verranno eseguiti ulteriori tentativi. Il numero di tentativi viene specificato dalla RetryCount proprietà e i tentativi verranno eseguiti dopo un ritardo specificato dalla RetryDelay proprietà . Se questi valori di proprietà non vengono impostati in modo esplicito, vengono applicati i valori predefiniti, ovvero 3 per la RetryCount proprietà e 250 ms per la RetryDelay proprietà. Per altre informazioni sul CachedImage controllo, vedere Memorizzazione nella cache delle immagini.

L'applicazione di riferimento eShopOnContainers implementa il modello di ripetizione dei tentativi. Per altre informazioni, inclusa una discussione su come combinare il modello di ripetizione dei tentativi con la HttpClient classe , vedere Microservizi .NET: Architettura per applicazioni .NET in contenitori.

Per altre informazioni sul modello di ripetizione dei tentativi, vedere il modello Di ripetizione dei tentativi .

Modello di interruttore

In alcune situazioni, gli errori possono verificarsi a causa di eventi previsti che richiedono più tempo per la correzione. Questi errori possono variare da una perdita parziale di connettività all'errore completo di un servizio. In queste situazioni, è inutile che un'app riprova a eseguire un'operazione con probabilità di esito positivo e deve invece accettare che l'operazione non sia riuscita e gestire di conseguenza questo errore.

Il modello di interruttore può impedire a un'app di tentare ripetutamente di eseguire un'operazione che potrebbe non riuscire, consentendo anche all'app di rilevare se l'errore è stato risolto.

Nota

Lo scopo del modello di interruttore è diverso dal modello di ripetizione dei tentativi. Il modello di ripetizione dei tentativi consente a un'app di ritentare un'operazione nel presupposto che abbia esito positivo. Il modello di interruttore impedisce a un'app di eseguire un'operazione che potrebbe non riuscire.

Un interruttore funge da proxy per le operazioni che potrebbero non riuscire. Il proxy deve monitorare il numero di errori recenti che si sono verificati e usare queste informazioni per decidere se consentire l'esecuzione dell'operazione o restituire immediatamente un'eccezione.

L'app per dispositivi mobili eShopOnContainers attualmente non implementa il modello di interruttore. Tuttavia, eShopOnContainers fa. Per altre informazioni, vedere Microservizi .NET: Architettura per applicazioni .NET in contenitori.

Suggerimento

Combinare i modelli di ripetizione e interruttore. Un'app può combinare i modelli di ripetizione e interruttore usando il modello di ripetizione dei tentativi per richiamare un'operazione tramite un interruttore. Tuttavia, la logica di riesecuzione deve essere sensibile alle eventuali eccezioni restituite dall'interruttore e abbandonare i tentativi di ripetizione se l'interruttore indica che un errore non è temporaneo.

Per altre informazioni sul modello di interruttore, vedere il modello interruttore .

Riepilogo

Molte soluzioni moderne basate sul Web usano servizi Web, ospitati da server Web, per fornire funzionalità per le applicazioni client remote. Le operazioni esposte da un servizio Web costituiscono un'API Web e le app client devono essere in grado di usare l'API Web senza sapere come vengono implementati i dati o le operazioni esposte dall'API.

Le prestazioni di un'app possono essere migliorate memorizzando nella cache i dati a cui si accede di frequente per l'archiviazione veloce che si trova vicino all'app. Le app possono implementare la memorizzazione nella cache in lettura con il modello cache-aside. Questo modello determina se l'elemento è attualmente presente nella cache. Se l'elemento non è presente nella cache, viene letto dall'archivio dati e aggiunto alla cache.

Quando si comunica con le API Web, le app devono essere sensibili agli errori temporanei. Gli errori temporanei includono la perdita momentanea della connettività di rete ai servizi, l'indisponibilità temporanea di un servizio o i timeout che si verificano quando un servizio è occupato. Questi errori sono spesso autocorribili e, se l'azione viene ripetuta dopo un ritardo appropriato, è probabile che abbia esito positivo. Di conseguenza, le app devono eseguire il wrapping di tutti i tentativi di accedere a un'API Web nel codice che implementa un meccanismo di gestione degli errori temporanei.