Condividi tramite


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 rappresentativo

Il trasferimento di stato rappresentativo (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 metodi di ReadAs, ad esempio ReadAsStringAsync e ReadAsByteArrayAsync, a seconda del formato dei dati.

Creazione di una richiesta GET

La classe CatalogService 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.

Recupero di dati dal microservizio del catalogo

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 classe RequestProvider per richiamare il metodo HTTP GET sulla risorsa, prima di restituire i risultati a CatalogViewModel. La classe RequestProvider 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 contenente 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 metodo GetAsync nella classe RequestProvider:

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 metodo CreateHttpClient, che restituisce un'istanza della classe HttpClient 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 metodo HandleResponse, 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 oggetto CatalogRoot e restituita a CatalogService.

Il metodo CreateHttpClient è 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 metodo CreateHttpClient, viene aggiunto all'intestazione Authorization di qualsiasi richiesta effettuata dall'istanza di HttpClient, preceduta 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 classe BasketService viene usata per gestire il processo di recupero e aggiornamento dei dati con il microservizio carrello. 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.

Invio di dati al microservizio basket

Figura 10-2: Invio di dati al microservizio basket

Quando un elemento viene aggiunto al carrello degli acquisti, viene chiamato il metodo ReCalculateTotalAsync nella classe BasketViewModel. Questo metodo aggiorna il valore totale degli elementi nel carrello e invia i dati del carrello al microservizio carrello, 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 metodo UpdateBasketAsync:

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 classe RequestProvider 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 metodi PostAsync nella classe RequestProvider:

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 metodo CreateHttpClient, che restituisce un'istanza della classe HttpClient 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 metodo HandleResponse, 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 classe RedisBasketRepository 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 CustomerBasket JSON formattata.

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

Eliminazione di dati dal microservizio basket

Figura 10-3: Eliminazione di dati dal microservizio basket

Quando viene richiamato il processo di estrazione, viene chiamato il metodo CheckoutAsync nella classe CheckoutViewModel. Questo metodo crea un nuovo ordine, prima di cancellare il carrello degli 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 metodo ClearBasketAsync:

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 metodo DeleteAsync nella classe RequestProvider:

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

Questo metodo chiama il metodo CreateHttpClient, che restituisce un'istanza della classe HttpClient 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 classe RedisBasketRepository 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 read-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 read-through 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 una 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 in modo da far scadere i dati, riducendo così il periodo in cui i dati possono risultare non aggiornati.

Suggerimento

Impostare un'ora di scadenza predefinita durante la configurazione di una cache. Molte cache implementano la scadenza, che invalida i dati e li rimuove dalla cache se non sono accessibili 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 la cache si riempia se i dati rimangono per un periodo troppo lungo. Pertanto, potrebbero essere necessarie richieste di aggiunta di nuovi elementi alla cache per rimuovere alcuni elementi in un processo noto come rimozione. I servizi di memorizzazione nella cache in genere eliminano i dati in base all’uso 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. Spesso questi errori si autocorreggono 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 è stata testata 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 ritorno a capo di tutti i tentativi di accesso a un servizio remoto in un codice che implementi lo schema 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 per l'operazione tentata. Se l'operazione fa parte di un'interazione dell'utente, l'intervallo di ripetizione dei tentativi deve essere breve e devono essere tentati solo pochi tentativi per evitare che gli utenti attendano 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 un tentativo e l’altro 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 che sta funzionando quasi o al massimo della sua 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 che ulteriori richieste vadano alla stessa risorsa e segnalare un fallimento. 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 di 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 riprovi a eseguire un'operazione con probabilità di esito positivo, mentre deve 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 nella speranza 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. Invece eShopOnContainers lo 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 read-through 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 spesso si autocorreggono 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 accesso a un'API Web nel codice che implementa un meccanismo di gestione degli errori temporanei.