Vale la pena di consentire l'iniezione di HttpClient nella mia libreria?

1

Sto creando una libreria per altri sviluppatori da utilizzare ed è per comunicare con un servizio web di terze parti. Ho letto il problema relativo a HttpClient e vorrei gestirlo correttamente, in modo che la libreria non diventi un peso.

Ho pensato di creare una base ServiceClient che avrebbe 3 modi per costruirlo.

Il primo istanzia il HttpClient stesso usando un static Lazy<HttpClient> per mantenere una singola istanza nella lib. Se questo costruttore non viene mai utilizzato, l'oggetto interno non viene mai istanziato.

Il secondo costruttore accetta un HttpMessageHandler e crea un'istanza di HttpClient con esso ogni volta. Sarebbe una specie di costruttore di mocking test-friendly che altrimenti non avrebbe molto senso.

Il terzo costruttore accetta un'istanza HttpClient , rendendo lo sviluppatore responsabile per la durata dell'oggetto, se questo è il loro desiderio.

Il mio pensiero iniziale era che uno sviluppatore potesse già utilizzare un HttpClient nel codice e che potesse passare la stessa istanza alla lib e impedire di avere più di un'istanza. Ma poi ho pensato che il client e la lib sarebbero stati in grado di comunicare con endpoint totalmente diversi e la configurazione della lib (base, intestazioni, ecc.) Sarebbe in conflitto con il codice chiamante, che non è utile a nessuno.

Quindi ora mi sto proponendo di abbandonare il terzo costruttore, perché non sono sicuro che porti qualcosa al tavolo.

Qualcuno ha esperienza con questo?

    
posta romeozor 31.07.2018 - 21:25
fonte

3 risposte

1

HttpClient non è realmente progettato per essere sostituito con un'alternativa (non ha una serie di interfacce astratte che sono generiche).

Quindi dubito che ci siano delle sostituzioni HttpClient conformi all'API. Ciò rende un candidato piuttosto scadente per il tipo di astrazione di cui stai parlando.

Le ragioni che potrebbero avere senso nella tua applicazione sono perché potrebbero esserci alcuni requisiti di upstream per la comunicazione che richiedono che i parametri siano passati o diversamente specificati. Ad esempio, potrebbe essere necessario specificare un nome utente / password e un certificato lato client o impostare altri parametri. È per questa ragione, principalmente, che consentire un argomento CTOR opzionale di tipo HttpClient ha probabilmente un senso.

Se si sceglie di fornire un parametro HttpClient facoltativo al costruttore, NON lo definisco in modo che il chiamante ne controlli la durata. Non ci sarebbe un modo semplice per coordinare le ipotesi sulla vita. Invece, consideralo un ADOPTION, dove è passata la tua libreria ADOPTS HttpClient.

    
risposta data 31.07.2018 - 21:42
fonte
0

Se non hai bisogno dell'interfaccia completa di HttpClient, farei un'altra strada: creerei un'interfaccia per HttpClient e permetterò all'utente di eseguire un'implementazione.

Puoi definire un'interfaccia che richiede solo i metodi effettivamente necessari da HttpClient , diciamo qualcosa del tipo:

public interface IHttpClient {
    public Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content);
}

e quindi offrire (e utilizzare) un wrapper:

public class HttpClientWrapper: IHttpClient {
    internal HttpClientWrapper(HttpClient client) {
        _client = client ?? new HttpClient();
    }

    public static FromHttpClient(HttpClient client) {
        return new HttpClientWrapper(client);
    }

    public async Task<HttpResponseMessage> PostAsync(string requestUri, HttpContent content) 
    {
        return await _client.PostAsync(requestUri, content);
    }

}

Ciò significa che puoi, in modo esplicito, definire quali parti di un HttpClient dipendono effettivamente; e, cosa più importante, se qualcuno vuole fare dei test automatici che usano la tua libreria, le effettive richieste http possono essere prese in giro con un'implementazione personalizzata.

    
risposta data 30.10.2018 - 16:32
fonte
0

In generale HttpMessageHandler è progettato per questo tipo di iniezione. Consente al codice di creazione dell'istanza di aggiungere le proprie modifiche al modo in cui vengono inviati i messaggi http. Ad esempio aggiungendo un'intestazione auth.

Ho scritto alcuni client API in cui un'istanza HttpClient è passata nel costruttore. Anche questo era un caso strano con una web api (A) che chiamava un'altra web api (B). Volevo ricreare l'oggetto client API per richiesta (su A) per trasmettere dati specifici della richiesta usando i parametri di costruzione (sul client B) evitando il problema di creare un oggetto HttpClient per richiesta (su A).

Ma davvero se sono onesto probabilmente non era la soluzione migliore. Volevo evitare di aggiungere un parametro aggiuntivo a tutti i metodi, cambiando l'interfaccia e questo è stato il modo che ha attirato la mia attenzione.

Penso che in un'altra situazione simile ho semplicemente passato un oggetto IGetPerRequestData che aveva accesso al contesto della richiesta, quindi sarebbe stato totalmente possibile fare il lavoro con un HttpMessageHandler personalizzato.

Nota a margine: non dovresti usare una statica per archiviare il gestore http, e se avessi due istanze che puntavano a diversi endpoint? semplicemente memorizzalo come un campo privato di sola lettura

public class MyApiClient
{
    private readonly HttpClient client;
    public MyApiClient(HttpMessageHandler handler = null)
    {
        if(handler != null) 
        { 
            this.client = new HttpClient(handler); 
        }
        else
        {
            client = new HttpClient();
        }
    }
}
    
risposta data 31.10.2018 - 22:14
fonte

Leggi altre domande sui tag