Ngày xuất bản: 6 tháng 10 năm 2020
Một số yêu cầu HTTP chứa tiêu đề Range:, cho biết rằng chỉ một phần của tài nguyên đầy đủ sẽ được trả về. Chúng thường dùng để truyền trực tuyến nội dung âm thanh hoặc video, cho phép tải các đoạn nội dung nghe nhìn nhỏ hơn theo yêu cầu, thay vì yêu cầu toàn bộ tệp từ xa cùng một lúc.
Trình chạy dịch vụ là mã JavaScript nằm giữa ứng dụng web và mạng, có khả năng chặn các yêu cầu mạng đi và tạo phản hồi cho các yêu cầu đó.
Trước đây, các yêu cầu về phạm vi và trình chạy dịch vụ không hoạt động hiệu quả cùng nhau. Bạn cần thực hiện các bước đặc biệt để tránh kết quả không mong muốn trong trình chạy dịch vụ. Rất may là điều này đang bắt đầu thay đổi. Trong các trình duyệt có hành vi chính xác, các yêu cầu về phạm vi sẽ "chỉ hoạt động" khi truyền qua một trình chạy dịch vụ.
Vấn đề của bạn là gì?
Hãy xem xét một trình chạy dịch vụ có trình nghe sự kiện fetch sau đây. Trình nghe này sẽ nhận mọi yêu cầu đến và truyền yêu cầu đó đến mạng:
self.addEventListener('fetch', (event) => {
// The Range: header will not pass through in
// browsers that behave incorrectly.
event.respondWith(fetch(event.request));
});
Trong các trình duyệt có hành vi không chính xác, nếu event.request bao gồm một tiêu đề Range:, thì tiêu đề đó sẽ bị loại bỏ một cách âm thầm. Yêu cầu mà máy chủ từ xa nhận được sẽ không bao gồm Range:. Điều này không nhất thiết sẽ "làm hỏng" bất cứ điều gì, vì về mặt kỹ thuật, máy chủ được phép trả về toàn bộ nội dung phản hồi, với mã trạng thái 200, ngay cả khi tiêu đề Range: xuất hiện trong yêu cầu ban đầu. Nhưng điều này sẽ dẫn đến việc truyền nhiều dữ liệu hơn mức cần thiết theo góc độ của trình duyệt.
Các nhà phát triển biết về hành vi này có thể giải quyết bằng cách kiểm tra rõ ràng sự hiện diện của tiêu đề Range: và không gọi event.respondWith() nếu có tiêu đề. Bằng cách này, trình chạy dịch vụ sẽ tự động loại bỏ chính nó khỏi quá trình tạo phản hồi và thay vào đó, logic mạng mặc định của trình duyệt (biết cách duy trì các yêu cầu về phạm vi) sẽ được sử dụng.
self.addEventListener('fetch', (event) => {
// Return without calling event.respondWith()
// if this is a range request.
if (event.request.headers.has('range')) {
return;
}
event.respondWith(fetch(event.request));
});
Tuy nhiên, có thể nói rằng hầu hết các nhà phát triển đều không biết rằng họ cần phải làm việc này. Và không rõ tại sao điều đó lại là bắt buộc. Cuối cùng, hạn chế này là do các trình duyệt cần bắt kịp những thay đổi trong quy cách cơ bản, giúp bổ sung khả năng hỗ trợ cho chức năng này.
Những vấn đề nào đã được khắc phục?
Các trình duyệt hoạt động đúng cách sẽ giữ lại tiêu đề Range: khi event.request được truyền đến fetch(). Điều này có nghĩa là mã trình chạy dịch vụ trong ví dụ ban đầu của tôi sẽ cho phép máy chủ từ xa thấy tiêu đề Range:, nếu tiêu đề đó được trình duyệt đặt:
self.addEventListener('fetch', (event) => {
// The Range: header will pass through in browsers
// that behave correctly.
event.respondWith(fetch(event.request));
});
Giờ đây, máy chủ có cơ hội xử lý đúng yêu cầu phạm vi và trả về một phần phản hồi có mã trạng thái 206.
Những trình duyệt nào hoạt động đúng cách?
Các phiên bản gần đây của Safari có chức năng chính xác. Chrome và Edge (kể từ phiên bản 87) cũng hoạt động bình thường.
Tính đến tháng 10 năm 2020, Firefox vẫn chưa khắc phục hành vi này, vì vậy, bạn vẫn cần phải tính đến hành vi này trong khi triển khai mã của trình chạy dịch vụ cho bản phát hành công khai.
Cách tốt nhất để xác nhận xem một trình duyệt cụ thể đã khắc phục hành vi này hay chưa là kiểm tra hàng "Include range header in network request" (Bao gồm tiêu đề phạm vi trong yêu cầu mạng) của trang tổng quan về Kiểm thử nền tảng web.
Còn việc phân phát các yêu cầu về phạm vi từ bộ nhớ đệm thì sao?
Service worker có thể làm được nhiều việc hơn là chỉ truyền yêu cầu đến mạng. Một trường hợp sử dụng phổ biến là thêm các tài nguyên, chẳng hạn như tệp âm thanh và video, vào bộ nhớ đệm cục bộ. Sau đó, một trình chạy dịch vụ có thể thực hiện các yêu cầu từ bộ nhớ đệm đó mà không cần đến mạng.
Tất cả trình duyệt, kể cả Firefox, đều hỗ trợ kiểm tra một yêu cầu bên trong trình xử lý fetch, kiểm tra sự hiện diện của tiêu đề Range:, sau đó thực hiện yêu cầu cục bộ bằng phản hồi 206 đến từ bộ nhớ đệm. Tuy nhiên, mã trình chạy dịch vụ để phân tích cú pháp chính xác tiêu đề Range: và chỉ trả về phân đoạn thích hợp của phản hồi hoàn chỉnh được lưu vào bộ nhớ đệm không phải là điều đơn giản.
Rất may là những nhà phát triển cần trợ giúp có thể chuyển sang Workbox. Đây là một tập hợp các thư viện giúp đơn giản hoá các trường hợp sử dụng phổ biến của trình chạy dịch vụ. workbox-range-request module triển khai tất cả logic cần thiết để phân phát các phản hồi một phần trực tiếp từ bộ nhớ đệm. Bạn có thể xem toàn bộ công thức cho trường hợp sử dụng này trong tài liệu Workbox.