Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Hiểu URI.join: Các Cạm Bẫy Phổ Biến Và Cách Dùng Đúng

Chúng ta vừa đạt một milestone tại Honeybadger. Sales pages của chúng ta không còn là một phần của main Rails app. Nó đã trên wish list của tôi nhiều năm, nhưng không chính xác top priority.

Là một phần của migration này, tôi thấy mình dùng URI.join để xây dựng các redirect links cụ thể. Nhưng tôi nhanh chóng chạy vào vấn đề. URI.join không behave như tôi mong đợi.

Tôi mong đợi nó lấy một đống path fragments và string chúng cùng nhau như sau:


# Đây là điều tôi mong đợi. Nó không xảy ra.
URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/plans/change"

Điều join method làm là lạ hơn nhiều. Nó drop một trong path fragments của tôi, chỉ dùng cái cuối, "change."


# Đây là điều xảy ra.
URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/change"

Vậy tại sao nó lại work như này?

Sự hiểu lầm

Rất thú vị, tôi đang mong đợi URI.join behave tương tự như phiên bản specialized của Array#join, lấy URL fragments và combine chúng để làm một whole URL.

Đó không phải điều nó làm. Big surprise.

Nếu chúng ta nhìn code của join method, chúng ta thấy nó chỉ iterate qua tất cả arguments, và gọi merge trên mỗi cái.


# File uri/rfc2396_parser.rb, line 236
def join(*uris)
  uris[0] = convert_to_uri(uris[0])
  uris.inject :merge
end

Merge method làm hai điều:

  1. Nó convert string của bạn như "pages" thành relative URI object.
  2. Nó cố gắng resolve relative URI lên base URI. Nó làm điều này chính xác theo cách specified trong RFC2396, Section 5.2.

Đó cool, nhưng nó giải thích hành vi bất ngờ tôi nhắc đến trước như thế nào?


URI.join("https://www.honeybadger.io", "plans", "change")
=> "https://www.honeybadger.io/change"

Hãy step through nó. Code trên tương đương với:


URI.parse("https://www.honeybadger.io/plans").merge("change")

Code trên cố gắng resolve relative URI, "change" against absolute URI "https://www.honeybadger.io/plans".

Để làm điều này, nó follow RFC2396, Section 5.2.6, which states:

a) All but the last segment of the base URI's path component is copied to the buffer. In other words, any characters after the last (right-most) slash character, if any, are excluded.

b) The reference's path component is appended to the buffer string.

Hãy chơi cùng:

  1. Copy mọi thứ trừ segment cuối của absolute URL. Đó cho tôi "https://www.honeybadger.io/"
  2. Append relative path, kết quả "https://www.honeybadger.io/change"

Thế giới lại có nghĩa!

Kết luận

Mặc dù URI.join có thể dùng để build URLs từ các path fragments, đó không thực sự là điều nó được designed để làm. Nó được designed để làm điều hơi phức tạp hơn: recursively merge URIs per standards specified in RFC.

Cái nhân project của tôi — building URLs để dùng trong redirects đến sales pages mới — well, tôi chỉ dùng Array#join thay thế. :)

EDIT 8/12/2016: Sau khi publish bài này tôi nhận được vài tweet gợi ý tôi dùng File.join cho mục đích này. Điều này có benefit tránh double slashes, i.e. /my//path nhưng sẽ break trên OSs như Windows, nơi path separator không phải forward-slash.