“Middleware” không phải là thuật ngữ mới trong điện toán. Nó thường được dùng như một thuật ngữ để mô tả một phần mềm giữ hai hệ thống lại với nhau. Bạn có thể gọi nó là “glue” cho phần mềm và về cơ bản, đó là cách phần mềm trung gian của Next.js hoạt động.
Phần mềm trung gian của Next.js cho phép bạn tạo các hàm thực thi sau khi yêu cầu của người dùng được thực hiện và trước khi yêu cầu hoàn tất — ở giữa hai quy trình. Điều này cho phép bạn xử lý yêu cầu của người dùng và sau đó sửa đổi phản hồi bằng cách viết lại, chuyển hướng, sửa đổi tiêu đề hoặc thậm chí là phát trực tuyến HTML.
Trong Next.js, phần mềm trung gian hoạt động trong thời gian chạy hạn chế được mô tả là “Edge Runtime”. Mã chạy qua thời gian chạy có quyền truy cập vào một tập hợp các API Web chuẩn, sẽ được thảo luận sau trong bài viết. Đối với khách hàng Vercel, các hàm trung gian sẽ được thực thi như Các hàm Edge của Vercel.
Điều này cũng có nghĩa là Trung gian có thể được giới hạn trong nhiều trang cho phép bạn tránh lặp lại mã. Ví dụ: nếu bạn cần thay đổi từng trang trong thư mục
Sự khác biệt kỹ thuật chính giữa hai tuyến này là các tuyến API của Next.js được thiết kế để lưu trữ trên một máy chủ nút đơn được lưu trữ tại một nơi, trong khi các hàm Middleware được thiết kế để triển khai trên "biên", về cơ bản là thuật ngữ tiếp thị để triển khai mã ở nhiều vị trí trên khắp thế giới. Bên cạnh sự khác biệt về khoảng cách vật lý, "biên" thường được liên kết với bộ đệm tích cực và việc vô hiệu hóa bộ đệm hiệu quả giúp giảm tính toán không cần thiết.
Mục tiêu của điều này là tốc độ. Phản hồi của máy chủ thường đến nhanh hơn khi người dùng ở gần máy chủ hơn, vì vậy khi bạn chỉ có một máy chủ, tốc độ đó chỉ có thể truy cập được đối với một nhóm người dùng con của bạn. Tuy nhiên, với việc mã của bạn được triển khai ở nhiều vị trí, nhiều người dùng hơn sẽ có quyền truy cập vào các phản hồi nhanh.
Cuối cùng, Middleware được thiết kế để không có thời gian khởi động nguội. Thời gian khởi động của tuyến API là nguyên nhân đáng kể khiến phản hồi chậm. Trên Vercel, các Hàm không máy chủ (được sử dụng để triển khai các tuyến API) thường mất khoảng 250 mili giây để khởi động. Middleware cũng được thiết kế để khởi động trong thời gian ngắn hơn nhiều so với các tuyến API, Vercel tuyên bố rằng Edge Functions của họ (được sử dụng để triển khai Next.js Middleware) có "khởi động nhanh hơn 100 lần" so với Serverless Functions của họ.
Điều này có thể hoạt động cùng với tính năng i8n / bản địa hóa của Next.js, như này.
Bạn cũng có thể muốn chặn quyền truy cập vào trang web của mình từ một số người dùng nhất định, chẳng hạn như bot hoặc người dùng ở một quốc gia nhất định. Để thực hiện điều này, bạn có thể trả về 404 có điều kiện hoặc viết lại yêu cầu thành trang "bị chặn". Vercel có ví dụ về chặn dựa trên vị trí tại đây.
Để đạt được điều này, bạn có thể đặt người dùng vào "thùng" thông qua cookie, sau đó chuyển hướng họ dựa trên thùng mà cookie của họ đặt họ vào. Xem ví dụ của Vercel để xem cách thức hoạt động của nó.
Một số hạn chế này chỉ dành riêng cho các triển khai Vercel của các trang Next.js, tuy nhiên, các hạn chế tương tự cũng tồn tại trên các nền tảng khác.
Điều này cũng có nghĩa là các mô-đun JavaScript dựa trên API Node.js gốc cũng không thể được sử dụng.
Để bắt đầu, hãy sao chép trình khởi động cho ứng dụng:
Trình khởi động có hai tệp chính:
Sau đó, chúng ta sẽ tạo hàm Middleware bằng cách tạo một tệp mới có tên
Sau đó, chúng ta sẽ cần nhập
Vì đối tượng
Chúng ta cũng cần nhập tệp tuyến đường:
Mỗi tệp phần mềm trung gian cần xuất một hàm có tên là
Hàm middleware sẽ được truyền qua một đối tượng yêu cầu. Tương tự như đối tượng
Thông qua đối tượng yêu cầu này, sau đó chúng ta có thể truy cập tên đường dẫn của yêu cầu hiện tại thông qua khóa
Đối với trình rút gọn liên kết của mình, chúng ta sẽ cần kiểm tra xem đối tượng
Sau đó, chúng ta có thể sử dụng đối tượng
Chúng ta đã tạo một đối tượng
Chúng ta cũng cần xử lý các trường hợp mà pathname không có đích phù hợp. Trong những trường hợp này, chúng tôi sẽ chuyển hướng người dùng đến trang chủ của mình:
Chúng tôi không thể chuyển hướng trực tiếp đến
Và chỉ như vậy, chúng tôi đã có một trình rút gọn liên kết đang hoạt động! Đối với những ai tò mò, toàn bộ hàm middleware của chúng tôi đã kết thúc như sau:
Toàn bộ cơ sở mã có sẵn tại https://github.com/sampoder/middleware-demo.
Mặc dù ví dụ này ngắn, nhưng nó cho thấy phần mềm trung gian có thể hữu ích như thế nào trong việc xây dựng mọi thứ. Khi bạn chạy ứng dụng web, bạn cũng sẽ thấy nó có thể nhanh đến mức nào.
Cuối cùng nhưng không kém phần quan trọng, phần mềm trung gian có rất nhiều hứa hẹn và tôi hy vọng bạn thích khám phá tính năng này cùng tôi!
Phần mềm trung gian của Next.js cho phép bạn tạo các hàm thực thi sau khi yêu cầu của người dùng được thực hiện và trước khi yêu cầu hoàn tất — ở giữa hai quy trình. Điều này cho phép bạn xử lý yêu cầu của người dùng và sau đó sửa đổi phản hồi bằng cách viết lại, chuyển hướng, sửa đổi tiêu đề hoặc thậm chí là phát trực tuyến HTML.
Trong Next.js, phần mềm trung gian hoạt động trong thời gian chạy hạn chế được mô tả là “Edge Runtime”. Mã chạy qua thời gian chạy có quyền truy cập vào một tập hợp các API Web chuẩn, sẽ được thảo luận sau trong bài viết. Đối với khách hàng Vercel, các hàm trung gian sẽ được thực thi như Các hàm Edge của Vercel.
Các tuyến API thì sao?
Khi bạn đọc bài viết này, bạn có thể nghĩ về cách phần mềm trung gian nghe giống hệt các tuyến API của Next.js đã tồn tại trong một thời gian. Sự khác biệt chính là cách chúng được sử dụng: thời gian chạy hạn chế hơn của các hàm trung gian, các yêu cầu riêng lẻ được thực hiện đối với các tuyến API, trong khi các hàm Trung gian hoạt động giữa yêu cầu của người dùng đối với một trang và trang đó đang được hiển thị.Điều này cũng có nghĩa là Trung gian có thể được giới hạn trong nhiều trang cho phép bạn tránh lặp lại mã. Ví dụ: nếu bạn cần thay đổi từng trang trong thư mục
ứng dụng
dựa trên việc người dùng có đăng nhập hay không, bạn có thể tạo một hàm Trung gian trong thư mục đó để xử lý cookie của người dùng để xem họ có đăng nhập hay không, sau đó truyền thông tin đó vào trang. Để so sánh, việc đạt được hiệu ứng tương tự sẽ cần thêm mã trong một tuyến API.Sự khác biệt kỹ thuật chính giữa hai tuyến này là các tuyến API của Next.js được thiết kế để lưu trữ trên một máy chủ nút đơn được lưu trữ tại một nơi, trong khi các hàm Middleware được thiết kế để triển khai trên "biên", về cơ bản là thuật ngữ tiếp thị để triển khai mã ở nhiều vị trí trên khắp thế giới. Bên cạnh sự khác biệt về khoảng cách vật lý, "biên" thường được liên kết với bộ đệm tích cực và việc vô hiệu hóa bộ đệm hiệu quả giúp giảm tính toán không cần thiết.
Mục tiêu của điều này là tốc độ. Phản hồi của máy chủ thường đến nhanh hơn khi người dùng ở gần máy chủ hơn, vì vậy khi bạn chỉ có một máy chủ, tốc độ đó chỉ có thể truy cập được đối với một nhóm người dùng con của bạn. Tuy nhiên, với việc mã của bạn được triển khai ở nhiều vị trí, nhiều người dùng hơn sẽ có quyền truy cập vào các phản hồi nhanh.
Cuối cùng, Middleware được thiết kế để không có thời gian khởi động nguội. Thời gian khởi động của tuyến API là nguyên nhân đáng kể khiến phản hồi chậm. Trên Vercel, các Hàm không máy chủ (được sử dụng để triển khai các tuyến API) thường mất khoảng 250 mili giây để khởi động. Middleware cũng được thiết kế để khởi động trong thời gian ngắn hơn nhiều so với các tuyến API, Vercel tuyên bố rằng Edge Functions của họ (được sử dụng để triển khai Next.js Middleware) có "khởi động nhanh hơn 100 lần" so với Serverless Functions của họ.
Khi nào tôi nên sử dụng Middleware?
Middleware nên được sử dụng trong các trường hợp cần xử lý một lượng nhỏ, điều này là do Middleware cần trả về phản hồi trong vòng chưa đầy 1,5 giây, nếu không thì yêu cầu sẽ hết thời gian chờ.Geolocation
Đối tượngNextRequest
có sẵn trong Middleware có thông tin địa lý có sẵn trong khóa geo
. Sử dụng thông tin này, sau đó bạn có thể viết lại người dùng của mình thành các trang có thông tin được bản địa hóa. Ví dụ: nếu bạn đang tạo trang web cho một chuỗi nhà hàng toàn cầu, bạn có thể hiển thị một menu khác nhau tùy thuộc vào vị trí của người dùng. ví dụ tại đây của Vercel sử dụng vị trí địa lý này để cung cấp Giá Power Parity.Điều này có thể hoạt động cùng với tính năng i8n / bản địa hóa của Next.js, như này.
Bảo mật
Thông qua đối tượngNextRequest
, thông tin cookie có sẵn (trên khóa cookies
) và bằng cách sử dụng NextResponse
, bạn có thể đặt cookie. Những cookie này có thể được sử dụng để xác thực người dùng trên trang web của bạn.Bạn cũng có thể muốn chặn quyền truy cập vào trang web của mình từ một số người dùng nhất định, chẳng hạn như bot hoặc người dùng ở một quốc gia nhất định. Để thực hiện điều này, bạn có thể trả về 404 có điều kiện hoặc viết lại yêu cầu thành trang "bị chặn". Vercel có ví dụ về chặn dựa trên vị trí tại đây.
Kiểm tra A/B
Trước đây, để hiển thị một trang khác cho người dùng trên một trang web tĩnh như một phần của thử nghiệm A/B (hoặc một bài tập tương tự), bạn sẽ phải xử lý yêu cầu của người dùng ở phía máy khách, điều này có thể gây ra các thay đổi bố cục tích lũy hoặc nhấp nháy. Tuy nhiên, nếu chúng ta xử lý nó trên máy chủ, điều này có thể tránh được.Để đạt được điều này, bạn có thể đặt người dùng vào "thùng" thông qua cookie, sau đó chuyển hướng họ dựa trên thùng mà cookie của họ đặt họ vào. Xem ví dụ của Vercel để xem cách thức hoạt động của nó.
Những hạn chế của phần mềm trung gian
Phần mềm trung gian nghe có vẻ khá tuyệt vời, phải không? Mặc dù tuyệt vời, nhưng vẫn có một số nhược điểm có nghĩa là bạn có thể vẫn cần các tuyến API cho một số trường hợp sử dụng nhất định.Một số hạn chế này chỉ dành riêng cho các triển khai Vercel của các trang Next.js, tuy nhiên, các hạn chế tương tự cũng tồn tại trên các nền tảng khác.
Thời gian thực thi (cụ thể là Vercel)
Một hàm phần mềm trung gian có thể thực thi trong tối đa ba mươi giây, tuy nhiên, như tôi đã đề cập ở trên, nó phải trả về phản hồi trong vòng một giây rưỡi. Điều này có nghĩa là hàm của bạn phải trả về phản hồi càng sớm càng tốt và sau đó bạn có thể tiếp tục bất kỳ khối lượng công việc nào khác ở chế độ nền nếu cần. Ví dụ, nếu bạn muốn thực hiện phân tích phía máy chủ, bạn có thể trích xuất thông tin cần thiết, trả về phản hồi, sau đó gọi đến cơ sở dữ liệu của mình để ghi lại thông tin sau khi trả về phản hồi.Kích thước hàm (cụ thể theo Vercel)
Một hàm Middleware có thể có kích thước tối đa là 1MB, bao gồm tất cả các mã khác được đóng gói cùng với hàm. Hầu hết các trường hợp sử dụng sẽ không yêu cầu một gói mã lớn như vậy, nhưng chắc chắn đây là điều cần chú ý.Các API Node.js gốc không được hỗ trợ
Các hàm Middleware không chạy qua Node.js như phần còn lại của mã phía máy chủ Next.js (chẳng hạn như API Routes). Một trong những điều quan trọng hạn chế các chức năng của Middleware thực hiện là đọc và ghi vào hệ thống tệp.Điều này cũng có nghĩa là các mô-đun JavaScript dựa trên API Node.js gốc cũng không thể được sử dụng.
Chỉ có ES Modules
Có thể sử dụng Node Modules trong middleware, tuy nhiên, chúng phải là ES Modules. Mặc dù có sự thay đổi ngày càng tăng trong hệ sinh thái để chuyển sang ES Modules, vẫn còn nhiều gói sử dụng CommonJS hoặc dựa vào các gói khác thông qua CommonJS.Không có Đánh giá Chuỗi
Cảeval
hoặc new Function(evalString)
của JavaScript đều không được phép trong thời gian chạy.Triển khai Middleware
Để khám phá cách Middleware hoạt động, chúng tôi sẽ tạo một trình rút gọn liên kết nhanh hơn nhiều so với các trình rút gọn sử dụng tuyến API.Để bắt đầu, hãy sao chép trình khởi động cho ứng dụng:
Mã:
yarn create next-app -e https://github.com/sampoder/middleware-demo/tree/starter
routes.js
& pages/index.js
. routes.js
sẽ chứa tất cả các tuyến đường cho trình rút gọn liên kết của chúng ta. Thông thường, bạn sẽ sử dụng cơ sở dữ liệu, nhưng với mục đích của bài tập này, chúng ta sẽ giữ cho nó đơn giản với một đối tượng khóa/giá trị được mã hóa cứng. pages/index.js
sẽ đóng vai trò là trang chủ của trình rút gọn liên kết của chúng ta với danh sách tất cả các tuyến đường khả dụng.Sau đó, chúng ta sẽ tạo hàm Middleware bằng cách tạo một tệp mới có tên
_middleware.js
trong thư mục pages
. Hàm middleware được giới hạn trong thư mục, ảnh hưởng đến các tuyến đường anh chị em và con. Ví dụ, vì thư mục /pages
được liên kết với các tuyến /
, do đó nếu phần mềm trung gian được đặt trong thư mục /pages
, nó sẽ áp dụng cho các tuyến như /about
hoặc /about/team/john
. Trong khi đó, nếu phần mềm trung gian được đặt trong thư mục /pages/blog
, nó sẽ áp dụng cho các tuyến đường, chẳng hạn như /blog/middleware
hoặc /blog/about/submit
, nhưng không áp dụng cho /info
.Sau đó, chúng ta sẽ cần nhập
NextResponse
từ next/server
:
Mã:
import { NextResponse } from 'next/server'
NextResponse
là phần mở rộng của giao diện Response
của Node.js, nên nó sẽ cho phép chúng ta sửa đổi phản hồi.Chúng ta cũng cần nhập tệp tuyến đường:
Mã:
import routes from "../routes"
middleware
. Đây sẽ là những gì Next.js chạy theo yêu cầu:
Mã:
export function middleware(req) {}
NextResponse
, đối tượng yêu cầu này là phần mở rộng của giao diện Request
của Node.js. Nó sẽ cung cấp cho chúng ta thông tin về yêu cầu của máy khách.Thông qua đối tượng yêu cầu này, sau đó chúng ta có thể truy cập tên đường dẫn của yêu cầu hiện tại thông qua khóa
nextUrl
:
Mã:
let { pathname } = req.nextUrl;
routes
của mình có chứa khóa có cùng giá trị với pathname hay không:
Mã:
if (routes[pathname]) {}
NextResponse
để sửa đổi phản hồi. Đối tượng NextResponse
cho phép chúng ta thực hiện cả phản hồi redirect()
và rewrite()
đến các vị trí khác nhau. Khi chúng ta đang xây dựng một trình rút gọn URL, chúng ta sẽ sử dụng phương thức redirect()
để chuyển người dùng đến đích mong muốn của họ:
Mã:
if (routes[pathname]) { return NextResponse.redirect(routes[req.nextUrl.pathname])}
NextResponse
mới, áp dụng phương thức chuyển hướng, sau đó trả về đối tượng đó.Chúng ta cũng cần xử lý các trường hợp mà pathname không có đích phù hợp. Trong những trường hợp này, chúng tôi sẽ chuyển hướng người dùng đến trang chủ của mình:
Mã:
else{ const url = request.nextUrl.clone() url.pathname = '/' return NextResponse.redirect(url)}
/
, vì hỗ trợ cho URL tương đối trong Middleware sẽ sớm bị loại bỏ. Thay vào đó, chúng tôi tạo bản sao của URL yêu cầu và thay đổi tên đường dẫn, trước khi truyền đối tượng URL đó cho hàm redirect()
.Và chỉ như vậy, chúng tôi đã có một trình rút gọn liên kết đang hoạt động! Đối với những ai tò mò, toàn bộ hàm middleware của chúng tôi đã kết thúc như sau:
Mã:
import { NextResponse } from "next/server";import routes from "../routes";export function middleware(req) { let { pathname } = req.nextUrl if (routes[pathname]) { return NextResponse.redirect(routes[req.nextUrl.pathname]) } else{ const url = request.nextUrl.clone() url.pathname = '/' return NextResponse.redirect(url) }}
Mặc dù ví dụ này ngắn, nhưng nó cho thấy phần mềm trung gian có thể hữu ích như thế nào trong việc xây dựng mọi thứ. Khi bạn chạy ứng dụng web, bạn cũng sẽ thấy nó có thể nhanh đến mức nào.
Cuối cùng nhưng không kém phần quan trọng, phần mềm trung gian có rất nhiều hứa hẹn và tôi hy vọng bạn thích khám phá tính năng này cùng tôi!
Đọc thêm
- Kết hợp ứng dụng web và ứng dụng gốc với 4 API JavaScript chưa biết
- AI thực sự có nghĩa là gì?
- Quốc tế hóa trong Next.js 13 với các thành phần máy chủ React
- Tạo biểu mẫu nhiều bước hiệu quả để có trải nghiệm người dùng tốt hơn