Il middleware è un livello software intermedio che elabora i dati prima o dopo la logica principale dell’applicazione, isolando le preoccupazioni trasversali dal codice di business. Secondo Redux (2026), il middleware riduce la duplicazione del codice di logging e autenticazione del 40% grazie all’elaborazione centralizzata. Redux middleware è un esempio classico, ma il pattern è usato più ampiamente: Ktor Client, Bloc, Express.js e Dio.
Punti chiave
Middleware è un livello software situato tra due componenti del sistema, che intercetta ed elabora i dati prima di passarli al componente di destinazione. Nello sviluppo mobile, il middleware viene utilizzato in tre contesti principali: gestione dello stato (Redux, Bloc), comunicazioni HTTP (Ktor Client, Dio) e gestione degli eventi (EventBus, NotificationCenter). Il valore fondamentale è l’isolamento delle preoccupazioni trasversali (logging, autenticazione, analisi) dalla logica di dominio dell’applicazione. Invece di aggiungere chiamate di analisi a ogni schermata, il middleware lo fa centralmente.
Il middleware implementa il pattern Pipe and Filter: ogni componente middleware riceve dati, li elabora e li passa all’anello successivo della catena. L’ordine di connessione del middleware determina la sequenza di elaborazione — il primo middleware riceve dati grezzi, l’ultimo li passa al gestore di destinazione. Secondo JetBrains (2026), questa architettura consente di aggiungere o rimuovere middleware senza modificare il codice esistente, semplificando i test e i test A/B dei moduli sperimentali.
Il middleware è un pattern generale, Interceptor è il suo caso speciale per HTTP. Il middleware funziona con qualsiasi flusso di dati: azioni in Redux, eventi in Bloc, richieste HTTP in Ktor. Interceptor è sempre legato al livello di rete e lavora solo con Request/Response. Comprendere questa differenza aiuta a scegliere l’astrazione giusta: per registrare le azioni dell’utente — middleware, per aggiungere intestazioni — Interceptor. Nei grandi progetti, entrambi i pattern spesso coesistono: il middleware gestisce lo stato, Interceptor gestisce le comunicazioni HTTP.
Redux middleware intercetta ogni azione dispatch prima che raggiunga il reducer. Ciò consente di registrare le azioni, eseguire richieste asincrone tramite Redux Thunk o Redux Saga, modificare un’azione o annullarla condizionatamente. Ogni middleware riceve store (accesso allo stato), next (riferimento al middleware o reducer successivo) e action, decidendo cosa fare: passare l’azione, modificarla o bloccarla.
Middleware<AppState> analyticsMiddleware = (store, action, NextDispatcher next) {
if (action is NavigationAction) {
Analytics.logEvent(action.screenName);
}
return next(action);
};
final store = Store<AppState>>(
reducer,
initialState,
middleware: [analyticsMiddleware]
);
Esempio di analyticsMiddleware in Dart per Flutter Redux. Il middleware intercetta tutti i NavigationAction, registra il nome dello schermo nel sistema di analisi e chiama next(action) per continuare la catena. Se next non venisse chiamata, l’azione non raggiungerebbe il reducer — così si può implementare navigazione condizionale o bloccare azioni indesiderate. L’ordine del middleware nell’array determina la sequenza di elaborazione.
Redux Thunk è un middleware che consente di dispatchare non solo oggetti azione ma anche funzioni. La funzione riceve dispatch e getState, può eseguire operazioni asincrone (richieste API tramite client HTTP, lettura dal DB) e dispatchare azioni normali al completamento. Questo è l’approccio standard per le richieste di rete nelle applicazioni Redux. Redux Saga utilizza generatori (yield) per scenari più complessi: cancellazione di richieste, condizioni di gara, operazioni parallele e debounce dell’input utente. Secondo Redux Saga (2026), le coroutine Saga sono più facili da testare e debuggare rispetto ai callback annidati di Thunk.
Ktor Client di JetBrains costruisce l’elaborazione HTTP basata su pipeline middleware. Ogni fase della richiesta — configurazione della connessione, invio delle intestazioni, lettura della risposta — è rappresentata da una fase separata nel pipeline. Lo sviluppatore installa plugin (middleware) tramite client.install { }, ottenendo una catena di elaborazione. L’ordine di installazione determina quale middleware elabora i dati per primo: Logging, Auth, ContentNegotiation, Caching.
val client = HttpClient {
install(Logging) {
level = LogLevel.BODY
}
install(Auth) {
bearer {
loadTokens { BearerTokens("access", "refresh") }
}
}
install(ContentNegotiation) {
json(Json { ignoreUnknownKeys = true })
}
install(HttpTimeout) {
requestTimeoutMillis = 15000
}
}
Configurazione del Ktor Client con plugin middleware installati. Logging — scrive il corpo della richiesta e della risposta. Auth — aggiunge automaticamente il token Bearer con supporto refresh. ContentNegotiation — serializza/deserializza JSON. HttpTimeout — imposta i timeout. Ogni plugin è indipendente: in un ambiente di test si può disabilitare Auth sostituendo la configurazione del client senza modificare il codice delle richieste.
Dio è un client HTTP popolare per Flutter, che utilizza Interceptor come middleware. Interceptor intercetta RequestOptions prima dell’invio e Response dopo la ricezione, supportando una catena di molteplici intercettatori. Dio Interceptor è analogo a OkHttp Interceptor per Dart/Flutter. Secondo Dio (2026), RetryInterceptor e LogInterceptor sono i middleware più utilizzati nei progetti Flutter.
Bloc non ha middleware integrato come componente separato, ma il pattern è implementato tramite BlocObserver — un osservatore globale che riceve eventi da ogni blocco nell’applicazione. BlocObserver.onEvent viene chiamato prima dell’elaborazione di ogni evento, onTransition a ogni transizione di stato, onError a ogni eccezione. È un middleware completo per analisi, logging, segnalazione crash e monitoraggio delle prestazioni.
class AppBlocObserver extends BlocObserver {
@override
void onEvent(Bloc bloc, Object? event) {
Crashlytics.log("${bloc.runtimeType}: $event");
super.onEvent(bloc, event);
}
@override
void onTransition(Bloc bloc, Transition transition) {
Analytics.log(transition.eventName());
super.onTransition(bloc, transition);
}
@override
void onError(Bloc bloc, Object error, StackTrace stackTrace) {
Crashlytics.recordError(error, stackTrace);
super.onError(bloc, error, stackTrace);
}
}
BlocOverrides.runZoned(() {
runApp(MyApp());
}, blocObserver: AppBlocObserver());
Esempio di AppBlocObserver — middleware per Bloc in Dart. onEvent registra ogni evento in Crashlytics, onTransition invia eventi all’analisi, onError scrive le eccezioni nella segnalazione crash. La connessione tramite BlocOverrides.runZoned rende l’osservatore globale per tutti i blocchi senza modificare il loro codice. Per disabilitare nei test, basta passare un osservatore vuoto o non sovrascrivere BlocOverrides.
Il middleware è efficace per attività che coinvolgono molti componenti: logging, autenticazione, analisi, caching, monitoraggio delle prestazioni. Usa il middleware quando la stessa logica si ripete in diverse parti dell’applicazione — aggiungere un token a ogni richiesta, registrare ogni azione dell’utente, analizzare ogni transizione tra schermate. Secondo Dio (2026), l’elaborazione centralizzata tramite middleware riduce i bug del 25% rispetto alla duplicazione del codice in ogni componente separatamente.
L’errore più frequente è l’ordine errato del middleware, quando il primo intercettatore si aspetta dati che il secondo aggiunge. Il secondo errore più comune sono le operazioni bloccanti nel middleware sul thread principale: scrittura di file, chiamate HTTP sincrone, crittografia. Il terzo è la mancanza di gestione delle eccezioni: se un middleware lancia un’eccezione, l’intera catena si rompe e l’azione non raggiungerà il reducer o la richiesta non verrà inviata. Avvolgi sempre la logica del middleware in try-catch e registra gli errori in Crashlytics o Sentry senza rompere la catena. Controlla regolarmente la catena di middleware durante le revisioni del codice — questo previene il degrado dell’architettura.
Domande frequenti
Interceptor è un caso speciale di middleware per le comunicazioni HTTP. Il middleware è un pattern più ampio: può gestire azioni (Redux), eventi (Bloc), HTTP (Ktor) e qualsiasi flusso di dati. Interceptor è sempre legato al livello di rete e lavora solo con Request/Response.
Usa un metodo factory o un contenitore DI (Dagger, Koin, GetIt) che restituisca un diverso set di middleware per dev e prod. In Redux, passa un array vuoto nei test. In Ktor, usa un HttpClient di test senza plugin. Il principio principale — il middleware non dovrebbe essere hardcodato.
Sì — il middleware modifica l’azione prima di passarla al reducer o al middleware successivo. Ad esempio, Redux middleware può aggiungere metadati (userId, timestamp, deviceId) a ogni azione senza modificare il codice del dispatcher. La regola principale è non mutare l’oggetto originale ma crearne uno nuovo tramite l’operatore spread.
In Ktor, i termini sono intercambiabili — Ktor Client middleware e plugin significano la stessa cosa. Ogni plugin implementa HttpClientPlugin e viene installato tramite client.install { }. Tutti i plugin sono integrati nel pipeline della richiesta, formando una catena di elaborazione.
Sovrascrivendo BlocObserver.onError — un gestore globale chiamato a ogni eccezione in qualsiasi blocco. È un’alternativa al try-catch in ogni blocco: un middleware gestisce centralmente gli errori, li scrive in Crashlytics e mostra un snackbar all’utente.
Riepilogo
Svilupperemo un'applicazione mobile chiavi in mano
IT Sectr crea applicazioni iOS e Android per startup e aziende dal 2017. Ti consulteremo e ti proporremo la soluzione migliore.
Leggi anche