Cùng một tên file nhưng đổi base URL từ /docs/ sang /docs lại khiến link trỏ sang chỗ khác. URL tương đối được phân giải theo cấu trúc URL, không phải nối hai chuỗi. Dấu gạch chéo cuối base quyết định phần đường dẫn nào được giữ khi ghép tham chiếu tương đối.
Bài viết dùng miền example.com và example.org chỉ để minh họa. Các ví dụ tạo URL trong bộ nhớ, không gửi request hoặc truy cập tài nguyên thật.
1. Base có và không có dấu / cuối
const directory = new URL('intro.html', 'https://example.com/docs/');
const document = new URL('intro.html', 'https://example.com/docs');
console.log(directory.href); // https://example.com/docs/intro.html
console.log(document.href); // https://example.com/intro.html
Với /docs/, thư mục hiện tại là /docs/. Với /docs, thành phần docs đứng sau dấu / cuối nên bị thay khi phân giải intro.html. Điều này không chứng minh server có file docs hay thư mục docs; parser không hỏi server để quyết định.
Khi thiết kế cấu hình đường dẫn tài nguyên, ghi rõ base là thư mục hay một URL tài liệu. Đừng tự thêm dấu / vào mọi URL vì query, fragment và ngữ nghĩa tài liệu có thể khiến cách sửa đó không phù hợp.
2. Dấu / đầu đưa đường dẫn về gốc origin
const base = 'https://example.com/app/docs/';
console.log(new URL('guide.html', base).pathname); // /app/docs/guide.html
console.log(new URL('/guide.html', base).pathname); // /guide.html
console.log(new URL('../guide.html', base).pathname); // /app/guide.html
Đường dẫn bắt đầu bằng / không giữ prefix /app/docs/. Đây là lỗi hay xuất hiện khi ứng dụng chạy dưới một thư mục con: code tưởng đang thêm đường dẫn vào base nhưng lại yêu cầu từ gốc website.
Ngược lại, ../ di chuyển theo thư mục của base. Chọn kiểu tham chiếu theo nơi đặt tài nguyên, không thêm hoặc bỏ dấu / chỉ để làm chuỗi trông đẹp hơn.
3. Query và fragment có quy tắc riêng
const base = 'https://example.com/docs/page.html?lang=vi#old';
console.log(new URL('?page=2', base).href);
// https://example.com/docs/page.html?page=2
console.log(new URL('#new', base).href);
// https://example.com/docs/page.html?lang=vi#new
console.log(new URL('', base).href);
// https://example.com/docs/page.html?lang=vi
Query mới thay query cũ và không giữ fragment cũ. Fragment mới giữ đường dẫn và query của base. Tham chiếu rỗng trong ví dụ giữ URL tài liệu cùng query nhưng bỏ fragment. Không suy luận bằng phép cộng chuỗi cho những trường hợp này.
Nếu cần thêm một tham số mà giữ các tham số khác, tạo URL rồi thao tác qua searchParams. Phân giải URL và mã hóa giá trị tham số là hai bước có contract khác nhau.
4. Base không ép mọi đầu vào ở cùng website
const base = 'https://example.com/app/';
console.log(new URL('https://example.org/news', base).href);
// https://example.org/news
console.log(new URL('//example.org/news', base).href);
// https://example.org/news
URL tuyệt đối chọn đích riêng thay vì dùng base để ghép đường dẫn. Tham chiếu bắt đầu bằng // lấy scheme từ base nhưng chọn host mới. Vì vậy truyền base không tự bảo đảm kết quả là link nội bộ.
Nếu tính năng chỉ cho phép một tập đích nhất định, hãy kiểm tra URL sau phân giải theo quy tắc sản phẩm: protocol, origin, thông tin đăng nhập và các ràng buộc khác khi cần. Origin trùng cũng không thay thế quyền truy cập từng tài nguyên. Ví dụ này không phải bộ kiểm tra redirect hoặc SSRF hoàn chỉnh.
5. Parse thành công chưa chứng minh tài nguyên tồn tại
try {
new URL('/guide.html');
} catch (error) {
console.log(error.name); // TypeError
}
const url = new URL('missing.html', 'https://example.com/docs/');
console.log(url.href); // https://example.com/docs/missing.html
Đường dẫn tương đối cần base hợp lệ. Trong khi đó missing.html tạo được URL không có nghĩa trang đó trả HTTP 200. Nếu ứng dụng cần xác nhận tài nguyên tồn tại, phải có bước kiểm tra phù hợp với loại tài nguyên và quyền truy cập.
URL constructor còn có thể chuyển kiểu đối số thành chuỗi. Ở biên API, hãy kiểm tra kiểu đầu vào rõ ràng nếu contract yêu cầu chuỗi, tránh biến giá trị thiếu thành một đường dẫn ngoài ý muốn.
6. Bộ ca kiểm tra đường dẫn nên có
- Base dạng thư mục có / cuối và dạng tài liệu không có / cuối.
- Đường dẫn thường, / từ gốc và ../ đi lên.
- Base đã có query và fragment.
- Tham chiếu chỉ gồm query, fragment hoặc chuỗi rỗng.
- URL tuyệt đối và tham chiếu // chọn host khác.
- Base thiếu hoặc không hợp lệ, cùng dữ liệu sai kiểu ở biên.
- Ứng dụng được triển khai ở gốc và dưới prefix thư mục con.
Các đoạn code đã chạy bằng Node.js local, không gọi mạng. Chưa kiểm thử router, thẻ base trong HTML hay cấu hình reverse proxy của ứng dụng thật. Tài liệu đối chiếu: URL constructor và phân giải tham chiếu tương đối.
Hãy kiểm tra URL kết quả thay vì chỉ đọc chuỗi đầu vào. Một dấu / nhỏ có thể thể hiện sự khác biệt giữa thư mục hiện tại và gốc website.




Chưa có bình luận. Hãy là người đầu tiên chia sẻ ý kiến.