URI
URI
URI는 Uniform Resource Identifier( RFC2396 )를 다루기 위한 클래스들을 제공하는 모듈이에요. 주소 문자열을 파싱해서 각 구성 요소에 접근하거나, 반대로 구성 요소를 조합해 주소를 만들 수도 있죠.
require 'uri'
uri = URI("http://foo.com/posts?id=30&limit=5#time=1305298413")
#=> #<URI::HTTP http://foo.com/posts?id=30&limit=5#time=1305298413>
uri.scheme #=> "http"
uri.host #=> "foo.com"
uri.path #=> "/posts"
uri.query #=> "id=30&limit=5"
uri.fragment #=> "time=1305298413"
uri.to_s #=> "http://foo.com/posts?id=30&limit=5#time=1305298413"
새로운 스킴(scheme)을 직접 등록할 수도 있어요. register_scheme로 클래스를 등록하면, 그 스킴을 가진 문자열을 파싱할 때 해당 클래스가 사용돼요.
module URI
class RSYNC < Generic
DEFAULT_PORT = 873
end
register_scheme 'RSYNC', RSYNC
end
#=> URI::RSYNC
URI.scheme_list
#=> {"FILE"=>URI::File, "FTP"=>URI::FTP, "HTTP"=>URI::HTTP,
# "HTTPS"=>URI::HTTPS, "LDAP"=>URI::LDAP, "LDAPS"=>URI::LDAPS,
# "MAILTO"=>URI::MailTo, "RSYNC"=>URI::RSYNC}
uri = URI("rsync://rsync.foo.com")
#=> #<URI::RSYNC rsync://rsync.foo.com>
RFC 사양을 직접 보시려면 www.ietf.org/rfc.html 을 참고하면 좋아요. 이 모듈과 관련된 RFC 목록이 정리되어 있으니까요.
출처: Ruby 3.3 API
본문
URI 모듈이 제공하는 주요 메서드를 하나씩 살펴볼게요.
decode_uri_component(str, enc = UTF_8)
URI.decode_www_form_component과 비슷하지만, '+'는 보존해요.
decode_www_form(str, sep = '&', use__charset_, isindex = false, enc = UTF_8) → array
주어진 문자열 str(ASCII 문자열이어야 해요)에서 이름/값 쌍을 뽑아내요. Net::HTTPResponse 객체 res의 본문 중 res['Content-Type']이 'application/x-www-form-urlencoded'인 경우를 디코딩할 때 주로 쓰죠.
반환값은 2개짜리 부분 배열들의 배열이에요. 각 부분 배열은 이름/값 쌍(둘 다 문자열)이고, 각 문자열은 enc 인코딩을 가지며 String#scrub으로 잘못된 문자는 제거돼요.
URI.decode_www_form('foo=0&bar=1&baz')
# => [["foo", "0"], ["bar", "1"], ["baz", ""]]
URI.decode_www_form_component과 비슷한 변환을 거치는데:
URI.decode_www_form('f%23o=%2F&b-r=%24&b+z=%40')
# => [["f#o", "/"], ["b-r", "$"], ["b z", "@"]]
구분자가 연속으로 나오는 경우도 허용돼요:
URI.decode_www_form('foo=0&&bar=1&&baz=2')
# => [["foo", "0"], ["", ""], ["bar", "1"], ["", ""], ["baz", "2"]]
다른 구분자를 지정할 수도 있어요:
URI.decode_www_form('foo=0--bar=1--baz', separator: '--')
# => [["foo", "0"], ["bar", "1"], ["baz", ""]]
decode_www_form_component(str, enc = UTF_8) → string
URL 인코딩된 문자열 str을 디코딩한 문자열을 돌려줘요. 먼저 String#b로 Encoding::ASCII-8BIT로 인코딩한 뒤, 아래처럼 디코딩하고, 마지막으로 주어진 인코딩 enc로 강제 변환해요.
반환 문자열은:
'*','.','-','_'문자와'a'..'z','A'..'Z','0'..'9'범위의 문자는 보존해요.URI.decode_www_form_component('*.-_azAZ09') # => "*.-_azAZ09"'+'는' '(공백)으로, 각 "percent notation"은 ASCII 문자로 변환해요.URI.decode_www_form_component('Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A') # => "Here are some punctuation characters: ,;?:"
관련: URI.decode_uri_component( '+' 보존).
encode_uri_component(str, enc = UTF_8)
URI.encode_www_form_component과 비슷하지만, ' '(공백)을 '+' 대신 '%20'으로 인코딩해요.
encode_www_form(enum, enc = UTF_8) → string
주어진 Enumerable enum에서 URL 인코딩된 문자열을 만들어요. 결과는 HTTP 요청의 폼 데이터로 쓰기 좋은데, Content-Type이 'application/x-www-form-urlencoded'인 경우에 해당하죠.
반환 문자열은 enum의 각 요소를 하나 이상의 URL 인코딩 문자열로 변환한 뒤 '&'로 모두 이어붙인 거예요.
URI.encode_www_form([['foo', 0], ['bar', 1], ['baz', 2]])
# => "foo=0&bar=1&baz=2"
URI.encode_www_form({foo: 0, bar: 1, baz: 2})
# => "foo=0&bar=1&baz=2"
문자 변환은 URI.encode_www_form_component를 사용해요:
URI.encode_www_form('f#o': '/', 'b-r': '$', 'b z': '@')
# => "f%23o=%2F&b-r=%24&b+z=%40"
enum이 배열처럼 생긴 경우, 각 요소 ele는 필드 하나로 변환되는데:
ele가 두 개 이상 요소의 배열이면 앞의 두 요소로 필드를 만들고(추가 요소는 무시):URI.encode_www_form([%w[foo bar], %w[baz bat bah]]) # => "foo=bar&baz=bat" URI.encode_www_form([['foo', 0], ['bar', :baz, 'bat']]) # => "foo=0&bar=baz"ele가 한 요소 배열이면ele[0]으로 필드를 만들고:URI.encode_www_form([['foo'], [:bar], [0]]) # => "foo&bar&0"- 그 외에는
ele자체로 필드를 만들어요:URI.encode_www_form(['foo', :bar, 0]) # => "foo&bar&0"
배열류 요소는 섞여 있어도 돼요:
URI.encode_www_form([['foo', 0], ['bar', 1, 2], ['baz'], :bat])
# => "foo=0&bar=1&baz&bat"
enum이 해시처럼 생긴 경우, 키/값 쌍을 하나 이상의 필드로 변환하는데:
- 값이 배열로 변환 가능하면, 값의 각 요소
ele를 키와 짝지어 필드를 만들고:URI.encode_www_form({foo: [:bar, 1], baz: [:bat, :bam, 2]}) # => "foo=bar&foo=1&baz=bat&baz=bam&baz=2" - 그 외에는 키와 값을 짝지어 필드를 만들어요:
URI.encode_www_form({foo: 0, bar: 1, baz: 2}) # => "foo=0&bar=1&baz=2"
해시류도 요소가 섞일 수 있어요:
URI.encode_www_form({foo: [0, 1], bar: 2})
# => "foo=0&foo=1&bar=2"
encode_www_form_component(str, enc = UTF_8) → string
주어진 문자열 str에서 URL 인코딩된 문자열을 만들어요.
반환 문자열은:
'*','.','-','_'문자와'a'..'z','A'..'Z','0'..'9'범위 문자는 보존해요.URI.encode_www_form_component('*.-_azAZ09') # => "*.-_azAZ09"' '(공백)은'+'로, 그 외 문자는 "percent notation"으로 변환해요. 문자c의 percent notation은'%%%X' % c.ord이에요.URI.encode_www_form_component('Here are some punctuation characters: ,;?:') # => "Here+are+some+punctuation+characters%3A+%2C%3B%3F%3A"
인코딩 규칙:
str의 인코딩이Encoding::ASCII_8BIT이면enc인자는 무시돼요.- 그 외에는
str을 먼저Encoding::UTF_8로 변환(문자 치환 포함)한 뒤 인코딩enc로 변환해요. - 어느 쪽이든 반환 문자열은
Encoding::US_ASCII로 강제 인코딩돼요.
관련: URI.encode_uri_component( ' '을 '%20'으로 인코딩).
for(scheme, *arguments, default: nil) → uri
주어진 scheme, arguments, default로 새 객체를 만들어요:
- 새 객체는
URI.scheme_list[scheme.upcase]의 인스턴스예요. scheme와arguments를 사용해서 클래스 초기화 메서드로 초기화돼요.URI::Generic.new를 참고하세요.
values = ['john.doe', 'www.example.com', '123', nil, '/forum/questions/', nil, 'tag=networking&order=newest', 'top']
URI.for('https', *values)
# => #<URI::HTTPS https://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
URI.for('foo', *values, default: URI::HTTP)
# => #<URI::HTTP foo://[email protected]:123/forum/questions/?tag=networking&order=newest#top>
join(*str) → uri
주어진 URI 문자열 str들을 RFC 2396 에 따라 합쳐요. 각 문자열은 합치기 전에 먼저 RFC3986 URI로 변환돼요.
URI.join("http://example.com/", "main.rbx")
# => #<URI::HTTP http://example.com/main.rbx>
URI.join('http://example.com', 'foo')
# => #<URI::HTTP http://example.com/foo>
URI.join('http://example.com', '/foo', '/bar')
# => #<URI::HTTP http://example.com/bar>
URI.join('http://example.com', '/foo', 'bar')
# => #<URI::HTTP http://example.com/bar>
URI.join('http://example.com', '/foo/', 'bar')
# => #<URI::HTTP http://example.com/foo/bar>
open(name, *rest) → file
URI를 포함한 여러 자원을 여는 것을 허용해요.
- 첫 인자가
open메서드에 응답하면, 나머지 인자들과 함께 그open을 호출해요. - 첫 인자가
(protocol)://로 시작하는 문자열이면URI.parse로 파싱하고, 파싱된 객체가open에 응답하면 나머지 인자들로 그open을 호출해요. - 그 외에는
Kernel#open을 호출해요.
OpenURI::OpenRead#open은 URI::HTTP#open, URI::HTTPS#open, URI::FTP#open, Kernel#open을 제공해요. http://, https://, ftp://로 시작하는 URI와 문자열을 받을 수 있고, 이 경우 열린 파일 객체는 OpenURI::Meta로 확장돼요.
parse(uri) → uri
주어진 문자열 uri로 새 URI 객체를 만들어요:
URI.parse('https://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
# => #<URI::HTTPS https://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
URI.parse('http://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
# => #<URI::HTTP http://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top>
uri에 유효하지 않은 URI 문자가 있을 수 있다면 먼저 ::escape를 사용하는 걸 권장해요.
register_scheme(scheme, klass) → klass
주어진 scheme의 URI를 파싱할 때 인스턴스화할 클래스로 klass를 등록해요:
URI.register_scheme('MS_SEARCH', URI::Generic) # => URI::Generic
URI.scheme_list['MS_SEARCH'] # => URI::Generic
scheme에 String#upcase를 적용한 뒤 그 결과가 유효한 상수 이름이어야 한다는 점을 기억하세요.
scheme_list → hash
정의된 스킴들의 해시를 돌려줘요:
URI.scheme_list
# =>
{"MAILTO"=>URI::MailTo,
"LDAPS"=>URI::LDAPS,
"WS"=>URI::WS,
"HTTP"=>URI::HTTP,
"HTTPS"=>URI::HTTPS,
"LDAP"=>URI::LDAP,
"FILE"=>URI::File,
"FTP"=>URI::FTP}
관련: URI.register_scheme.
split(uri) → array
문자열 uri로 만든 URI의 구성 요소를 나타내는 9개짜리 배열을 돌려줘요. 각 요소는 문자열이거나 nil이에요:
names = %w[scheme userinfo host port registry path opaque query fragment]
values = URI.split('https://***@www.example.com:123/forum/questions/?tag=networking&order=newest#top')
names.zip(values)
# =>
[["scheme", "https"],
["userinfo", "john.doe"],
["host", "www.example.com"],
["port", "123"],
["registry", nil],
["path", "/forum/questions/"],
["opaque", nil],
["query", "tag=networking&order=newest"],
["fragment", "top"]]