Sự khác biệt giữa URI.escape và CGI.escape là gì?


147

Sự khác biệt giữa những gì URI.escapeCGI.escapevà cái nào tôi nên sử dụng?

Câu trả lời:


124

Có một số khác biệt nhỏ, nhưng điểm quan trọng là URI.escapeđã bị phản đối trong Ruby 1.9.2 ... vì vậy hãy sử dụng CGI::escapehoặc ERB :: Util.url_encode .

Có một cuộc thảo luận dài về ruby-core dành cho những người quan tâm cũng đề cập đến WEBrick :: HTTPUtils.escapeWEBrick :: HTTPUtils.escape_form .


11
Chỉ để thêm vào sự nhầm lẫn - Tôi vừa thấy một nhận xét về stackoverflow.com/questions/4967608/ , nơi ai đó đã đề cập rằng lối thoát cgi sử dụng '+' thay vì% 20 cho khoảng trắng và nó chống lại 'spec' ...
Louis Sayers

18
một cách khác là sử dụng ERB::Util.url_encodeđúng cách %20 cho các không gian
riffraff

1
@Ernest: Xem: github.com/ruby/ruby/commit/ (câu trả lời được cập nhật)
Marc-André Lafortune

4
ruby-doc.org/stdlib-2.0.0/libdoc/uri/rdoc/URI/Escape.html . Có mô-đun URI.escape trong ruby ​​2.0.0. Tại sao nó bị phản đối?
dùng938363

1
@ user938363 nếu bạn nhấp vào nguồn hiển thị trên đó, bạn sẽ thấy nó vẫn bị đánh dấu là không dùng nữa.
thu hút

229

Sự khác biệt giữa rìu và kiếm và tôi nên sử dụng loại nào? Vâng, nó phụ thuộc vào những gì bạn cần làm.

URI.escapeđược cho là mã hóa một chuỗi (URL) thành cái gọi là " Mã hóa phần trăm ".

CGI::escapeđến từ thông số CGI , mô tả cách dữ liệu nên được mã hóa / giải mã giữa máy chủ web và ứng dụng.

Bây giờ, hãy nói rằng bạn cần thoát URI trong ứng dụng của mình. Đây là một trường hợp sử dụng cụ thể hơn. Cho rằng, cộng đồng Ruby đã sử dụng URI.escapetrong nhiều năm. Vấn đề với URI.escapelà nó không thể xử lý thông số RFC-3896.

URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog' 
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog"

URI.escape được đánh dấu là lỗi thời:

Hơn nữa, URI.encode hiện tại là gsub đơn giản. Nhưng tôi nghĩ nó nên tách một URI thành các thành phần, sau đó thoát từng thành phần và cuối cùng tham gia chúng.

Vì vậy, URI.encode hiện tại được coi là có hại và không dùng nữa. Điều này sẽ được loại bỏ hoặc thay đổi hành vi quyết liệt.

Sự thay thế tại thời điểm này là gì?

Như tôi đã nói ở trên, URI.encode hiện tại sai ở cấp độ spec. Vì vậy, chúng tôi sẽ không cung cấp sự thay thế chính xác. Sự thay thế sẽ thay đổi tùy theo trường hợp sử dụng của nó.

https://bugs.ruby-lang.org/issues/4167

Thật không may, không có một từ nào về nó trong các tài liệu, cách duy nhất để biết về nó là kiểm tra nguồn hoặc chạy tập lệnh với các cảnh báo ở cấp độ dài (-wW2 ) (hoặc sử dụng một số google-fu).

Một số đề xuất sử dụng CGI::Escapecho các tham số truy vấn, vì bạn không thể thoát toàn bộ URI:

CGI::escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http%3A%2F%2Fgoogle.com%2Ffoo%3Fbar%3Dat%23anchor%26title%3DMy+Blog+%26+Your+Blog"

CGI::escapechỉ nên được sử dụng cho các tham số truy vấn, nhưng kết quả sẽ lại, so với thông số kỹ thuật. Trên thực tế, trường hợp sử dụng phổ biến nhất là thoát dữ liệu biểu mẫu, chẳng hạn như trong khi gửi mộtapplication/x-www-form-urlencoded yêu cầu POST.

Cũng được đề cập WEBrick::HTTPUtils.escapelà không có nhiều cải tiến (một lần nữa, nó chỉ là một đơn giản gsub, đó là IMO, thậm chí là một lựa chọn tồi tệ hơn URI.escape):

WEBrick::HTTPUtils.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at%23anchor&title=My%20Blog%20&%20Your%20Blog" 

Gần nhất với thông số kỹ thuật dường như là viên ngọc Địa chỉ :

require 'addressable/uri'
Addressable::URI.escape 'http://google.com/foo?bar=at#anchor&title=My Blog & Your Blog'
# => "http://google.com/foo?bar=at#anchor&title=My%20Blog%20&%20Your%20Blog"

Lưu ý rằng, không giống như tất cả các tùy chọn trước đó, Địa chỉ không thoát #, và đây là hành vi dự kiến. bạn muốn giữ #hàm băm trong đường dẫn URI nhưng không phải trong truy vấn URI.

Vấn đề duy nhất còn lại là chúng tôi đã không thoát khỏi các tham số truy vấn của mình một cách chính xác, điều này đưa chúng ta đến kết luận: chúng ta không nên sử dụng một phương thức duy nhất cho toàn bộ URI, vì cho đến nay vẫn chưa có giải pháp hoàn hảo. Như bạn thấy &đã không thoát khỏi "Blog của tôi và Blog của bạn". Chúng tôi cần sử dụng một hình thức thoát khác nhau cho các tham số truy vấn, nơi người dùng có thể đặt các ký tự khác nhau có ý nghĩa đặc biệt trong URL. Nhập mã hóa URL. Mã hóa URL nên được sử dụng cho mọi giá trị truy vấn "đáng ngờ", tương tự như những gì ERB::Util.url_encode:

ERB::Util.url_encode "My Blod & Your Blog"
# => "My%20Blod%20%26%20Your%20Blog""

Thật tuyệt nhưng chúng tôi đã yêu cầu Địa chỉ:

uri = Addressable::URI.parse("http://www.go.com/foo")
# => #<Addressable::URI:0x186feb0 URI:http://www.go.com/foo>
uri.query_values = {title: "My Blog & Your Blog"}
uri.normalize.to_s
# => "http://www.go.com/foo?title=My%20Blog%20%26%20Your%20Blog"

Phần kết luận:

  • Không được dùng URI.escape hoặc tương tự
  • Sử dụng CGI::escape nếu bạn chỉ cần thoát mẫu
  • Nếu bạn cần làm việc với các URI, hãy sử dụng Địa chỉ, nó cung cấp mã hóa URL, mã hóa biểu mẫu và bình thường hóa URL.
  • Nếu đó là một dự án Rails, hãy xem " Làm cách nào để URL thoát chuỗi trong Rails? "

Cảm ơn rất nhiều cho các thông tin. Nó chắc chắn đã thoát khỏi một số cảnh báo thử nghiệm cuốc. Một cái cào và một cái cuốc nhìn ra bên dưới.
Douglas G. Allen

Giải thích tuyệt vời @Ernest, nhưng vấn đề với điều này là nó không hoạt động đối với các URL bên ngoài mà tôi không cố gắng tạo (và không có quyền kiểm soát). ví dụ: trình thu thập thông tin đọc URL từ một trang web và sau đó cố gắng truy cập các URL đó (cần được mã hóa trước khi truy cập).
amit_saxena

@amit_saxena nếu bạn có đủ khả năng Addressablelà một trong những viên ngọc của mình, trước tiên bạn có thể phân tích URL, fi rubydoc.info/gems/addressable/Addressable/URI.heuristic_parse
Ernest

Hấp dẫn! Nhưng một lần nữa, tôi không thể lấy băm các tham số từ url gốc bằng cách sử dụng mã này, sau đó tôi mã hóa như bạn mô tả. Luồng trong trường hợp của tôi là: Tôi nhận được các url bên ngoài từ một số nguồn cấp dữ liệu -> sau đó tôi cần mã hóa -> Chuyển đến ứng dụng khách http để tìm nạp nội dung. Bây giờ nếu tôi không mã hóa các url bên ngoài đúng cách, các máy khách HTTP dựa trên ruby ​​sẽ thất bại với các lỗi URI không hợp lệ.
amit_saxena

Phương thức phân tích cú pháp @amit_saxena sẽ trả về thể hiện của Addressable:URL, sau đó bạn có thể gọi tất cả các phương thức cá thể trên đó, có thể một trong số chúng sẽ mang lại cho bạn kết quả mong muốn: rubydoc.info/gems/addressable/Addressable/URI
Ernest


6

CGI::escapelà tốt để thoát khỏi đoạn văn bản để chúng có thể được sử dụng trong các tham số truy vấn url (chuỗi sau '?'). Ví dụ: nếu bạn muốn có tham số chứa các ký tự gạch chéo trong url, bạn CGI :: thoát chuỗi đó trước rồi chèn nó vào url.

Tuy nhiên, trong Rails có lẽ bạn sẽ không được sử dụng trực tiếp. Thông thường bạn sử dụng hash.to_param, sẽ sử dụng CGI::escapedưới mui xe.


URI::escapelà tốt để thoát một url không được thoát đúng. Ví dụ: một số trang web xuất url sai / không thoát trong thẻ neo của họ. Nếu chương trình của bạn sử dụng các url này để lấy thêm tài nguyên, OpenURI sẽ phàn nàn rằng các url không hợp lệ. Bạn cần URI::escapenhững thứ này để biến nó thành một url hợp lệ. Vì vậy, nó được sử dụng để thoát khỏi toàn bộ chuỗi URI để làm cho nó phù hợp. Theo từ của tôi, URI :: unescape làm cho một url có thể đọc được bởi con người và URI :: esc làm cho nó hợp lệ với các trình duyệt.

Đây là thuật ngữ giáo dân của tôi và cảm thấy thoải mái để sửa những điều đó.


1

Sự khác biệt là URI.escape không hoạt động ...

CGI.escape"/en/test?asd=qwe"
=> "%2Fen%2Ftest%3Fasd%3Dqwe"

URI.escape"/en/test?asd=qwe"
=> "/en/test?asd=qwe"

2
Bạn đã chọn trường hợp kiểm tra sai .. Các / ',?' Và = 'đều là một phần của URI hợp lệ và do đó không được thoát. Các ký tự khác cần được thoát đặc biệt là trong chuỗi truy vấn.
Gerard ONeill

@GerardONeill Tôi đã chọn trường hợp thử nghiệm chính xác để cho thấy URI.escape không hoạt động và không đáng tin cậy. Bạn có gợi ý rằng URI.escape chỉ thoát khỏi chuỗi truy vấn không? Làm thế nào nó có thể biết khi nào một giá trị tham số kết thúc nếu tôi muốn mã hóa một & trong đó? có lẽ đó là lý do tại sao nó đã lỗi thời?
Radu Simionescu

1
Đó chính xác là những gì tôi đang nói. Lối thoát URI phải phân tích URL, tách biệt những gì nó nghĩ là các tham số riêng lẻ, thoát chúng và đặt chúng lại với nhau. Thậm chí điều đó có thể lộn xộn. Nhưng nó không làm điều đó - nó chỉ tránh thoát một số nhân vật trong khi thoát khỏi phần còn lại, điều đó làm cho nó không hoàn chỉnh. Nó có thể được sử dụng cho các trường hợp đơn giản, đặc biệt nếu bạn biết rằng các tham số của mình sẽ không gây nhầm lẫn.
Gerard ONeill

0

CGI.escape là để thoát một giá trị URL trong chuỗi truy vấn. Tất cả các ký tự không rơi vào ALPHA, DIGIT, '_', '-', '.' và bộ ký tự '' được thoát.

Nhưng điều đó sẽ làm cho một URL không chính xác, vì một url cần phải có '/', ':', '?', '[', '&', '=' Và ';'. Có lẽ nhiều hơn tôi không thể nghĩ ra khỏi đỉnh đầu của tôi.

URI.escape để các ký tự URL đó một mình và cố gắng tìm các khóa và giá trị chuỗi truy vấn để thoát. Tuy nhiên, điều này thực sự không thể phụ thuộc vào vì các giá trị có thể có tất cả các loại ký tự ngăn lối thoát dễ dàng. Về cơ bản, đã quá muộn. Nhưng nếu URL có thể phụ thuộc vào đơn giản (không có '&' và '=' v.v. trong các giá trị), thì hàm này có thể được sử dụng để thoát các ký tự có thể không đọc được hoặc không hợp lệ.

Nói chung - luôn luôn sử dụng CGI.escape trên các khóa và giá trị riêng lẻ trước khi nối chúng với '&' và thêm chúng sau '?'.


0

CGI.escape không hoạt động với API OpenProject. Nó mã hóa [] ,: chứ không phải +. Tôi đã hack cái này với nhau dường như hoạt động cho API của OpenProject. Nhưng tôi chắc chắn rằng nó thiếu một số .subub. Nó có thể gần như tồi tệ như URI.escape, nhưng nó sẽ không cung cấp cho bạn các lỗi lỗi thời.

class XXX
      def self.encode(path)
        path, query = path.split("?", 2)
        return path if query.nil?
        query = CGI.escape(query).gsub("%3A", ":").gsub("%3D","=").gsub("%5B","[").gsub("%5D","]").gsub("%2C",",").gsub("+","%20")
        return [path,query].join("?")
      end
end

XXX.encode("http://test.com/some/path?query=[box: \"cart\"]")
URI.encode("http://test.com/some/path?query=[box: \"cart\"]")

Cả hai đầu ra:

=> " http://test.com/some/path?query=[box:%20%22cart%22] "
=> " http://test.com/some/path?query=[box:%20 % 22cart% 22] "

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.