Middleware là một lớp phần mềm trung gian xử lý dữ liệu trước hoặc sau logic chính của ứng dụng, cô lập các mối quan tâm xuyên suốt khỏi mã nghiệp vụ. Theo Redux (2026), middleware giảm sự trùng lặp mã ghi log và xác thực xuống 40% nhờ xử lý tập trung. Redux middleware là một ví dụ cổ điển, nhưng mẫu này được sử dụng rộng rãi hơn: Ktor Client, Bloc, Express.js và Dio.
Những điểm chính
Middleware là một lớp phần mềm nằm giữa hai thành phần hệ thống, chặn và xử lý dữ liệu trước khi chuyển đến thành phần đích. Trong phát triển di động, middleware được sử dụng trong ba ngữ cảnh chính: quản lý trạng thái (Redux, Bloc), giao tiếp HTTP (Ktor Client, Dio) và xử lý sự kiện (EventBus, NotificationCenter). Giá trị cốt lõi là sự cô lập các mối quan tâm xuyên suốt (ghi log, xác thực, phân tích) khỏi logic miền của ứng dụng. Thay vì thêm lệnh gọi phân tích vào mỗi màn hình, middleware thực hiện điều đó một cách tập trung.
Middleware triển khai mẫu Pipe and Filter: mỗi thành phần middleware nhận dữ liệu, xử lý và chuyển cho mắt xích tiếp theo trong chuỗi. Thứ tự kết nối middleware xác định trình tự xử lý — middleware đầu tiên nhận dữ liệu thô, middleware cuối cùng chuyển cho trình xử lý đích. Theo JetBrains (2026), kiến trúc này cho phép thêm hoặc xóa middleware mà không thay đổi mã hiện có, đơn giản hóa việc kiểm thử và kiểm thử A/B các mô-đun thử nghiệm.
Middleware là một mẫu chung, Interceptor là trường hợp đặc biệt của nó cho HTTP. Middleware hoạt động với mọi luồng dữ liệu: actions trong Redux, sự kiện trong Bloc, yêu cầu HTTP trong Ktor. Interceptor luôn gắn với lớp mạng và chỉ làm việc với Request/Response. Hiểu sự khác biệt này giúp chọn đúng sự trừu tượng: để ghi log hành động người dùng — middleware, để thêm tiêu đề — Interceptor. Trong các dự án lớn, cả hai mẫu thường cùng tồn tại: middleware quản lý trạng thái, Interceptor xử lý giao tiếp HTTP.
Redux middleware chặn mọi action dispatch trước khi nó đến reducer. Điều này cho phép ghi log các action, thực hiện các yêu cầu bất đồng bộ qua Redux Thunk hoặc Redux Saga, sửa đổi action hoặc hủy nó có điều kiện. Mỗi middleware nhận store (quyền truy cập trạng thái), next (tham chiếu đến middleware hoặc reducer tiếp theo) và action, quyết định làm gì: chuyển action, sửa đổi hoặc chặn.
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]
);
Ví dụ về analyticsMiddleware trong Dart cho Flutter Redux. Middleware chặn tất cả NavigationAction, ghi tên màn hình vào hệ thống phân tích và gọi next(action) để tiếp tục chuỗi. Nếu next không được gọi, action sẽ không đến được reducer — bằng cách này có thể triển khai điều hướng có điều kiện hoặc chặn các action không mong muốn. Thứ tự middleware trong mảng xác định trình tự xử lý.
Redux Thunk là middleware cho phép dispatch không chỉ các đối tượng action mà còn cả hàm. Hàm nhận dispatch và getState, có thể thực hiện các thao tác bất đồng bộ (yêu cầu API qua máy khách HTTP, đọc từ DB) và dispatch các action thông thường khi hoàn thành. Đây là cách tiếp cận tiêu chuẩn cho các yêu cầu mạng trong ứng dụng Redux. Redux Saga sử dụng trình tạo (yield) cho các kịch bản phức tạp hơn: hủy yêu cầu, điều kiện đua, thao tác song song và debounce đầu vào người dùng. Theo Redux Saga (2026), các coroutine Saga dễ kiểm thử và gỡ lỗi hơn so với callback lồng nhau của Thunk.
Ktor Client của JetBrains xây dựng xử lý HTTP dựa trên pipeline middleware. Mỗi giai đoạn yêu cầu — thiết lập kết nối, gửi tiêu đề, đọc phản hồi — được đại diện bởi một pha riêng biệt trong pipeline. Nhà phát triển cài đặt plugin (middleware) qua client.install { }, nhận được một chuỗi xử lý. Thứ tự cài đặt xác định middleware nào xử lý dữ liệu đầu tiên: 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
}
}
Cấu hình Ktor Client với các plugin middleware đã cài đặt. Logging — ghi nội dung yêu cầu và phản hồi. Auth — tự động thêm token Bearer với hỗ trợ refresh. ContentNegotiation — tuần tự hóa/giải tuần tự hóa JSON. HttpTimeout — đặt thời gian chờ. Mỗi plugin độc lập: trong môi trường kiểm thử có thể vô hiệu hóa Auth bằng cách thay thế cấu hình máy khách mà không thay đổi mã yêu cầu.
Dio là máy khách HTTP phổ biến cho Flutter, sử dụng Interceptor làm middleware. Interceptor chặn RequestOptions trước khi gửi và Response sau khi nhận, hỗ trợ chuỗi nhiều bộ chặn. Dio Interceptor tương tự như OkHttp Interceptor cho Dart/Flutter. Theo Dio (2026), RetryInterceptor và LogInterceptor là các middleware được sử dụng nhiều nhất trong các dự án Flutter.
Bloc không có middleware tích hợp như một thành phần riêng biệt, nhưng mẫu được triển khai qua BlocObserver — một quan sát viên toàn cục nhận sự kiện từ mọi bloc trong ứng dụng. BlocObserver.onEvent được gọi trước khi xử lý mỗi sự kiện, onTransition tại mỗi chuyển đổi trạng thái, onError tại mỗi ngoại lệ. Đây là một middleware hoàn chỉnh cho phân tích, ghi log, báo cáo sự cố và giám sát hiệu suất.
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());
Ví dụ về AppBlocObserver — middleware cho Bloc trong Dart. onEvent ghi mỗi sự kiện vào Crashlytics, onTransition gửi sự kiện đến phân tích, onError ghi ngoại lệ vào báo cáo sự cố. Kết nối qua BlocOverrides.runZoned làm cho quan sát viên trở nên toàn cục cho tất cả các bloc mà không thay đổi mã của chúng. Để vô hiệu hóa trong kiểm thử, chỉ cần truyền một quan sát viên trống hoặc không ghi đè BlocOverrides.
Middleware có hiệu quả cho các tác vụ ảnh hưởng đến nhiều thành phần: ghi log, xác thực, phân tích, lưu cache, giám sát hiệu suất. Sử dụng middleware khi cùng một logic lặp lại ở các phần khác nhau của ứng dụng — thêm token vào mỗi yêu cầu, ghi log mỗi hành động người dùng, phân tích mỗi chuyển đổi màn hình. Theo Dio (2026), xử lý tập trung qua middleware giảm lỗi 25% so với việc sao chép mã trong mỗi thành phần riêng lẻ.
Lỗi phổ biến nhất là thứ tự middleware không đúng, khi bộ chặn đầu tiên mong đợi dữ liệu mà bộ chặn thứ hai thêm vào. Lỗi phổ biến thứ hai là các thao tác chặn trong middleware trên luồng chính: ghi tệp, lệnh gọi HTTP đồng bộ, mã hóa. Thứ ba là thiếu xử lý ngoại lệ: nếu middleware ném ngoại lệ, toàn bộ chuỗi bị đứt và action sẽ không đến được reducer hoặc yêu cầu sẽ không được gửi. Luôn bọc logic middleware trong try-catch và ghi lỗi vào Crashlytics hoặc Sentry mà không làm đứt chuỗi. Thường xuyên kiểm tra chuỗi middleware trong các buổi đánh giá mã — điều này ngăn chặn sự suy thoái kiến trúc.
Các câu hỏi thường gặp
Interceptor là trường hợp đặc biệt của middleware cho giao tiếp HTTP. Middleware là mẫu rộng hơn: nó có thể xử lý actions (Redux), sự kiện (Bloc), HTTP (Ktor) và mọi luồng dữ liệu. Interceptor luôn gắn với lớp mạng và chỉ làm việc với Request/Response.
Sử dụng phương thức nhà máy hoặc vùng chứa DI (Dagger, Koin, GetIt) trả về một bộ middleware khác nhau cho dev và prod. Trong Redux, truyền một mảng trống trong kiểm thử. Trong Ktor, sử dụng HttpClient kiểm thử không có plugin. Nguyên tắc chính — middleware không nên được mã hóa cứng trong code.
Có — middleware sửa đổi action trước khi chuyển đến reducer hoặc middleware tiếp theo. Ví dụ, Redux middleware có thể thêm siêu dữ liệu (userId, timestamp, deviceId) vào mỗi action mà không thay đổi mã của bộ điều phối. Nguyên tắc chính là không làm thay đổi đối tượng gốc mà tạo một đối tượng mới qua toán tử spread.
Trong Ktor, các thuật ngữ có thể thay thế cho nhau — Ktor Client middleware và plugin có cùng ý nghĩa. Mỗi plugin triển khai HttpClientPlugin và được cài đặt qua client.install { }. Tất cả các plugin được nhúng vào pipeline yêu cầu, tạo thành một chuỗi xử lý.
Bằng cách ghi đè BlocObserver.onError — một trình xử lý toàn cục được gọi tại mỗi ngoại lệ trong bất kỳ bloc nào. Đây là một giải pháp thay thế cho try-catch trong mỗi bloc: một middleware tập trung xử lý lỗi, ghi chúng vào Crashlytics và hiển thị snackbar cho người dùng.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm