Chúng tôi vừa đạt được một cột mốc quan trọng tại Honeybadger. Các trang bán hàng của chúng tôi không còn là một phần của ứng dụng Rails chính của chúng tôi. Nó đã nằm trong danh sách mong muốn của tôi trong nhiều năm, nhưng không hẳn là ưu tiên hàng đầu.
Là một phần của quá trình di chuyển này, tôi thấy mình đang sử dụng URI.join
để xây dựng các liên kết chuyển hướng cụ thể. Nhưng tôi nhanh chóng gặp phải một vấn đề. URI.join
không hoạt động như tôi mong đợi.
Tôi mong đợi nó sẽ lấy một loạt các đoạn đường dẫn và xâu chuỗi chúng lại với nhau như vậy:
# This is what I was expecting. It didn't happen.
URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/plans/change"
join
là gì phương pháp đã làm là xa lạ hơn nhiều. Nó đã làm rơi một trong các đoạn đường dẫn của tôi, chỉ sử dụng đoạn cuối cùng, "thay đổi".
# This is what happened.
URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/change"
Vậy tại sao nó lại hoạt động như thế này?
Sự hiểu lầm
Hóa ra là tôi đã mong đợi URI.join
để hoạt động tương tự như một phiên bản chuyên biệt của Array#join
, lấy các đoạn URL và kết hợp chúng để tạo thành một URL toàn bộ.
Đó không phải là những gì nó làm. Bất ngờ lớn.
Nếu chúng ta xem xét join
mã của phương thức, chúng tôi thấy rằng nó chỉ lặp lại trên tất cả các đối số và gọi merge
trên mỗi.
# File uri/rfc2396_parser.rb, line 236
def join(*uris)
uris[0] = convert_to_uri(uris[0])
uris.inject :merge
end
Phương thức hợp nhất thực hiện hai việc:
- Nó chuyển đổi chuỗi của bạn như "các trang" thành một đối tượng URI tương đối.
- Nó cố gắng giải quyết URI tương đối dựa trên URI cơ sở. Nó thực hiện điều này chính xác theo cách được chỉ định trong RFC2396, Phần 5.2.
Thật tuyệt, nhưng nó giải thích thế nào về hành vi không mong muốn mà tôi đã đề cập trước đây?
URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/change"
Hãy bước qua nó. Đoạn mã trên tương đương với:
URI.parse("https://www.honeybadger.io/plans").merge("change")
Đoạn mã trên cố gắng giải quyết URI tương đối, "thay đổi" so với URI tuyệt đối "https://www.honeybadger.io/plans".
Để thực hiện việc này, nó tuân theo RFC2396, Phần 5.2.6, trong đó nêu rõ:
a) Tất cả trừ đoạn cuối cùng của thành phần đường dẫn của URI cơ sở được sao chép vào bộ đệm. Nói cách khác, bất kỳ ký tự nào sau ký tự gạch chéo cuối cùng (ngoài cùng bên phải), nếu có, đều bị loại trừ.
b) Thành phần đường dẫn của tham chiếu được nối vào chuỗi bộ đệm.
Hãy chơi cùng:
- Sao chép mọi thứ trừ phân đoạn cuối cùng của URL tuyệt đối. Điều đó mang lại cho tôi
"https://www.honeybadger.io/"
- Nối đường dẫn tương đối, dẫn đến
"https://www.honeybadger.io/change"
Thế giới lại có ý nghĩa!
Kết luận
Trong khi URI.join
có thể được sử dụng để tạo URL từ các đoạn đường dẫn khác nhau, đó không thực sự là những gì nó được thiết kế để làm. Nó được thiết kế để làm điều gì đó phức tạp hơn một chút:hợp nhất đệ quy các URI theo các tiêu chuẩn được chỉ định trong RFC.
Đối với dự án cá nhân của tôi - xây dựng các URL để sử dụng trong chuyển hướng đến các trang bán hàng mới của chúng tôi - tốt, tôi chỉ sử dụng Array # join. :)
CHỈNH SỬA 8/12/2016: Sau khi xuất bản bài viết này, tôi đã nhận được một vài tweet đề nghị tôi sử dụng File.join
vì mục đích này. Điều này có lợi là tránh được gạch chéo đôi, tức là. /my//path
nhưng sẽ bị hỏng trên hệ điều hành như Windows, nơi dấu phân cách đường dẫn không phải là dấu gạch chéo.