Condividi tramite


Accesso ai dati remoti

Suggerimento

Questo contenuto è un estratto dell'eBook, Enterprise Application Patterns Using .NETMAUI, disponibile in .NET Docs o come PDF scaricabile gratuitamente che può essere letto offline.

Enterprise Application Patterns Using .NET MAUI eBook cover thumbnail.

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 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 (Not Found).

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 Accetta 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 API in Microsoft Docs.

Uso delle API RESTful

L'app multipiattaforma eShopOnContainers usa il modello Model-View-ViewModel (MVVM) e gli elementi del criterio 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 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.

Effettuare richieste Web

L'app multipiattaforma eShopOnContainers usa la classe HttpClient 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 classe HttpResponseMessage 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 classe HttpContent 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.

Esecuzione di una richiesta GET

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

L'immagine seguente mostra l'interazione delle classi che leggono i dati del catalogo dal microservizio catalogo per la visualizzazione da parte di CatalogView.

Retrieving data from the catalog microservice.

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

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

Questo metodo chiama il metodo GetCatalogAsync dell'istanza di CatalogService inserita in CatalogViewModel dal contenitore di inserimento delle dipendenze. 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;          
} 

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 classe RequestProvider viene inserita nella classe CatalogService, vedere dependency injection.

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 = GetOrCreateHttpClient(token);
    HttpResponseMessage response = await httpClient.GetAsync(uri);

    await HandleResponse(response);
    TResult result = await response.Content.ReadFromJsonAsync<TResult>();

    return result;
}

Questo metodo chiama il metodo GetOrCreateHttpClient, che restituisce un'istanza della classe HttpClient con le intestazioni appropriate impostate. Invia quindi una richiesta di GET asincrona alla risorsa identificata dall'URI, con la risposta archiviata nell'istanza di 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 GetOrCreateHttpClient è illustrato nell'esempio di codice seguente:

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

Questo metodo usa la creazione di una nuova istanza o recupera un'istanza memorizzata nella cache della classe HttpClient e imposta l'intestazione Accept di tutte le richieste effettuate dall'istanza di 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 GetOrCreateHttpClient, viene aggiunto all'intestazione Authorization di qualsiasi richiesta effettuata dall'istanza di HttpClient, preceduta dalla stringa Bearer. Per altre informazioni sull'autorizzazione, vedere Autorizzazione.

Suggerimento

È consigliabile memorizzare nella cache e riutilizzare le istanze dI HttpClient per migliorare le prestazioni dell'applicazione. La creazione di un nuovo HttpClient per ogni operazione può causare problemi con l'esaurimento del socket. Per altre informazioni, vedere Creazione di istanze HttpClient nel Centro per sviluppatori Microsoft.

Quando il metodo GetAsync nella classe RequestProvider chiama HttpClient.GetAsync, viene richiamato il metodo Items nella classe CatalogController 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.

Esecuzione di una richiesta POST

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

L'immagine seguente mostra l'interazione delle classi che inviano i dati del carrello, visualizzati da BasketView, al microservizio carrello.

Sending data to the basket microservice.

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()
{
    // Omitted for brevity...

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

Questo metodo chiama il metodo UpdateBasketAsync dell'istanza di BasketService che è stata inserita in BasketViewModel dal contenitore di inserimento delle dipendenze. 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 carrello. 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 = 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;
}

Questo metodo chiama il metodo GetOrCreateHttpClient, 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 in un oggetto CustomerBasket e restituita a BasketService. Per altre informazioni sul metodo GetOrCreateHttpClient, vedere Creazione di una richiesta GET.

Quando il metodo PostAsync nella classe RequestProvider chiama HttpClient.PostAsync, viene richiamato il metodo Post nella classe BasketController 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 di ELIMINAZIONE

L'immagine seguente mostra le interazioni delle classi che eliminano i dati del carrello dal microservizio carrello, per CheckoutView.

Deleting data from the basket microservice.

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()
{
    // Omitted for brevity...

    await _basketService.ClearBasketAsync(
        _shippingAddress.Id.ToString(), authToken);
}

Questo metodo chiama il metodo ClearBasketAsync dell'istanza di BasketService inserita in CheckoutViewModel dal contenitore di inserimento delle dipendenze. Il metodo seguente illustra il metodo ClearBasketAsync:

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

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 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 carrello. 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 = GetOrCreateHttpClient(token);
    await httpClient.DeleteAsync(uri);
}

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

Quando il metodo DeleteAsync nella classe RequestProvider chiama HttpClient.DeleteAsync, viene richiamato il metodo Delete nella classe BasketController 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 in Microsoft Docs.

Suggerimento

Memorizzare nella cache di frequente i dati letti e di rado le modifiche.

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 multipiattaforma 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 quello dell’uso più di recente e quello del first-in-first-out. Per altre informazioni, vedere guida alla memorizzazione nella cache in Microsoft Docs.

Memorizzazione nella cache delle immagini

L'app multipiattaforma eShopOnContainers usa immagini di prodotti remoti che traggono vantaggio dalla memorizzazione nella cache. Queste immagini vengono visualizzate dal controllo Immagine. Il controllo Immagine .NET MAUI supporta la memorizzazione nella cache delle immagini scaricate con 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 proprietà CacheValidity. Per altre informazioni, vedere Memorizzazione nella cache delle immagini scaricate nel Centro per sviluppatori Microsoft.

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 Modello di interruttore.

Suggerimento

Non implementare mai un meccanismo a ciclo infinito. Preferisce invece un backoff esponenziale.

Usare un numero finito di tentativi o implementare il modello di interruttore per consentire il ripristino di un servizio.

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 classe HttpClient, 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 in Microsoft Docs.

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.

Al momento, l'app multipiattaforma eShopOnContainers 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 in Microsoft Docs.

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.