ZetCode

Ruby Net::HTTP 教程

最后修改于 2023 年 10 月 18 日

在本文中,我们将介绍如何使用标准的 Ruby Net::HTTP 模块。我们获取数据、提交数据、处理 JSON,并连接到安全的网页。本教程在多个示例中使用了 Sinatra 应用程序。ZetCode 还有一个简洁的 Ruby 教程

超文本传输协议 (HTTP) 是一种用于分布式、协作式、超媒体信息系统的应用协议。HTTP 是万维网数据通信的基础。

Ruby Net::HTTP 提供了一个丰富的库,可用于构建 HTTP 客户端。

Ruby Net:HTTP 状态

响应的 codemessage 方法会给出其状态。

status_code.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI 'http://webcode.me'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

uri = URI 'http://www.example.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

uri = URI 'http://www.urbandicionary.com/define.php?term=Dog'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

我们使用 get_response 方法执行三个 HTTP 请求,并检查返回的状态。

uri = URI 'http://www.example.com/news/'
res = Net::HTTP.get_response uri
puts res.message
puts res.code

HTTP 响应的状态通过 messagecode 方法进行检查。

$ ./status_code.rb 
OK
200
Not Found
404
Found
302

200 是成功 HTTP 请求的标准响应,404 表示找不到请求的资源,302 表示资源被暂时重定向。

Ruby Net::HTTP HEAD 请求

HEAD 请求是一种 HTTP GET 请求,但不包含消息正文。请求/响应的头部包含元数据,例如 HTTP 协议版本或内容类型。

head 方法检索文档头信息。头信息由字段组成,包括日期、服务器、内容类型或最后修改时间。

head_req.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "http://www.webcode.me"
http = Net::HTTP.new uri.host, uri.port

res = http.head '/'

puts res['server']
puts res['date']
puts res['last-modified']
puts res['content-type']
puts res['content-length']

该示例打印 www.something.com 网页的服务器、日期、最后修改时间、内容类型和内容长度。

$ ./http_head.rb 
nginx/1.6.2
Wed, 03 Feb 2021 10:30:30 GMT
Sat, 20 Jul 2019 11:49:25 GMT
text/html
348

Ruby Net::HTTP GET 请求

HTTP GET 方法请求指定资源的表示形式。

get_content.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI 'http://www.webcode.me'

content = Net::HTTP.get uri

puts content

该脚本抓取 webcode.me 网页的内容。net/http 设计用于与 uri 模块紧密配合。

require 'net/http'

这也需要 uri,因此我们无需单独 require 它。

content = Net::HTTP.get uri

get 方法向指定的资源生成 GET 请求。

$ ./http_get.rb 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My html page</title>
</head>
<body>

    <p>
        Today is a beautiful day. We go swimming and fishing.
    </p>
    
    <p>
         Hello there. How are you?
    </p>
    
</body>
</html>

以下程序获取一个小型网页并去除其 HTML 标签。

strip_tags.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "http://example.com"

doc = Net::HTTP.get uri

puts doc.gsub %r{</?[^>]+?>}, ''

该脚本会剥离 example 网页的 HTML 标签。

puts doc.gsub %r{</?[^>]+?>}, ''

使用一个简单的正则表达式来去除 HTML 标签。

Ruby Net::HTTP 异步请求

在下面的示例中,我们进行多个异步请求。

async_req.rb
#!/usr/bin/ruby

require 'async'
require 'net/http'
require 'uri'

urls = [
  'http://webcode.me', 
  'https://example.com',
  'http://httpbin.org',
  'https://ruby-lang.cn'
]

Async do
  urls.each do |url|
    Async do
      res = Net::HTTP.get_response(URI url)
      puts "#{res.message} #{res.code}"
    end
  end
end

我们向四个站点发送非阻塞 Web 请求,并打印它们的响应状态码和消息。

$ ./async_req.rb 
OK 200
OK 200
OK 200
OK 200

Sinatra

Sinatra 是一个流行的 Ruby Web 应用程序框架。它易于安装和设置。我们的一些示例也将使用 Sinatra 应用程序。

$ sudo gem install sinatra
$ sudo gem install thin

我们安装 Sinatra 和 Thin Web 服务器。如果安装了 Thin,Sinatra 会自动选择 Thin 而不是默认的 WEBrick 服务器。

main.rb
#!/usr/bin/ruby

require 'sinatra'

get '/' do
    "First application"
end

该应用程序响应 / 路由。它将一个简单的消息发送回客户端。

$ ruby main.rb 
== Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from Thin
2021-02-03 11:36:49 +0100 Thin web server (v1.8.0 codename Possessed Pickle)
2021-02-03 11:36:49 +0100 Maximum connections set to 1024
2021-02-03 11:36:49 +0100 Listening on localhost:4567, CTRL+C to stop

应用程序通过 ruby main.rb 命令启动。Thin 服务器将被启动;它监听 4567 端口。

$ curl localhost:4567
First application

使用 curl 命令行工具,我们连接到服务器并访问 / 路由。控制台会显示一条消息。

Sinatra get 方法

get 方法向服务器发出 GET 请求。GET 方法请求指定资源的表示形式。

main.rb
#!/usr/bin/ruby

require 'sinatra'

get '/greet' do  
    "Hello #{params[:name]}"
end

这是 Sinatra 应用程序文件。在收到 /greet 路由后,它会返回一条包含客户端发送的名称的消息。

client_get.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "https://:4567/greet"

params = { :name => 'Peter' }
uri.query = URI.encode_www_form params

puts Net::HTTP.get uri

该脚本将一个带有值的变量发送到 Sinatra 应用程序。该变量直接在 URL 中指定。

params = { :name => 'Peter' }

这是我们发送到服务器的参数。

uri.query = URI.encode_www_form params

我们使用 encode_www_form 方法将参数编码到 URL 中。

puts Net::HTTP.get uri

get 方法向服务器发送 GET 请求。它会返回打印到控制台的响应。

$ ./client_get.rb 
Hello Peter

这是示例的输出。

::1 - - [03/Feb/2021:11:51:46 +0100] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0046

在 Thin 服务器的此日志中,我们可以看到参数已被编码到 URL 中。

我们可以直接将参数放入 URL 字符串中。

client_get2.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "https://:4567/greet?name=Peter"

puts Net::HTTP.get uri 

这是发出 GET 消息的另一种方式;它与上一个示例本质上是相同的。

Sinatra get 用户代理

在本节中,我们指定了用户代理的名称。

main.rb
#!/usr/bin/ruby

require 'sinatra'

get '/agent' do
    request.user_agent
end

Sinatra 应用程序返回客户端发送的用户代理。

client_agent.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "https://:4567"
http = Net::HTTP.new uri.host, uri.port

res = http.get '/agent', {'User-Agent' => 'Ruby script'}
puts res.body

此脚本向 Sinatra 应用程序创建了一个简单的 GET 请求。

res = http.get '/agent', {'User-Agent' => 'Ruby script'}

用户代理在 get 方法的第二个参数中指定。

$ ./client_agent.rb 
Ruby script

服务器响应了我们随请求发送的代理名称。

Sinatra 提交一个值

post 方法在给定的 URL 上分派一个 POST 请求,为表单填写内容提供键/值对。

main.rb
#!/usr/bin/ruby

require 'sinatra'

post '/target' do
    "Hello #{params[:name]}"
end

Sinatra 应用程序在 /target 路由上返回问候语。它从 params 哈希中获取值。

client_post.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI "https://:4567/target"

params = { :name => 'Peter' }
res = Net::HTTP.post_form uri, params

puts res.body

该脚本发送一个带有键 name,值为 Peter 的请求。POST 请求通过 Net::HTTP.post_form 方法发出。

$ ./client_post.rb 
Hello Peter
::1 - - [03/Feb/2021:12:00:54 +0100] "POST /target HTTP/1.1" 200 11 0.0015

使用 POST 方法时,值不会发送在请求 URL 中。

从字典中检索定义

在下面的示例中,我们在 www.dictionary.com 上查找一个术语的定义。

$ sudo gem install nokogiri

要解析 HTML,我们使用 nokogiri gem。

get_term.rb
#!/usr/bin/ruby

require 'net/http'
require 'nokogiri'

term = 'cat'
uri = URI "https://www.dictionary.com/browse/#{term}"

res = Net::HTTP.get uri

doc = Nokogiri::HTML res
doc.css("span.one-click-content").map do |node|
    s = node.text.strip!
    s.gsub!(/\s{3,}/, " ") unless (s == nil)
    puts s unless (s == nil)
end

在此脚本中,我们在 www.dictionary.com 上查找术语 cat 的定义。Nokogiri::HTML 用于解析 HTML 代码。

uri = URI "https://www.dictionary.com/browse/#{term}"

要执行搜索,我们将术语附加到 URL 的末尾。

doc = Nokogiri::HTML res
doc.css("span.one-click-content").map do |node|
    s = node.text.strip!
    s.gsub!(/\s{3,}/, " ") unless (s == nil)
    puts s unless (s == nil)
end

我们使用 Nokogiri::HTML 类解析内容。我们通过删除过多的空格来改进格式。

JSON

JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。它易于人类阅读和编写,也易于机器解析和生成。

$ sudo gem install json

如果我们之前没有安装 json gem,我们需要安装它。

main.rb
require 'sinatra'
require 'json'

get '/example.json' do
    content_type :json
    { :name => 'Jane', :age => 17 }.to_json
end

Sinatra 应用程序发送 JSON 数据。它使用 to_json 方法来完成此操作。

parse_json.rb
#!/usr/bin/ruby

require 'net/http'
require 'json'
 
uri = URI 'https://:4567/example.json'
res = Net::HTTP.get uri 

data = JSON.parse res

puts data["name"]
puts data["age"]

该示例读取 Sinatra 应用程序发送的 JSON 数据。

$ ./parse_json.rb 
Jane
17

接下来,我们从 Ruby 脚本向 Sinatra 应用程序发送 JSON 数据。

main.rb
require 'sinatra'
require 'json'

post '/readjson' do
    data = JSON.parse request.body.read
    "#{data["name"]} is #{data["age"]} years old"
end

此应用程序读取 JSON 数据并发送回包含解析值的消息。

post_json.rb
#!/usr/bin/ruby

require 'net/http'
require 'json'
 
uri = URI 'https://:4567/readjson'

req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}
req.body = {:name => 'Jane', :age => 17}.to_json

res = Net::HTTP.start(uri.hostname, uri.port) do |http|
    http.request req
end

puts res.body

此脚本向 Sinatra 应用程序发送 JSON 数据并读取其响应。

req = Net::HTTP::Post.new uri.path, initheader = {'Content-Type' =>'application/json'}

'application/json' 内容类型必须在请求的标头中指定。

$ ./post_json.rb 
Jane is 17 years old

重定向

重定向是将一个 URL 转发到另一个 URL 的过程。HTTP 响应状态码 302 用于临时 URL 重定向。

main.rb
require 'sinatra'

get "/oldpage" do  
    redirect to("/files/newpage.html"), 302
end

在 Sinatra 应用程序中,我们使用 redirect 命令重定向到另一个位置。

public/files/newpage.html
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>

这是位于 public/files 子目录中的 newpage.html 文件。

redirect.rb
#!/usr/bin/ruby

require 'net/http'

uri = URI 'https://:4567/oldpage'

res = Net::HTTP.get_response uri
if res.code == "302"
    res = Net::HTTP.get_response URI res.header['location']
end 

puts res.body

此脚本访问旧页面并跟随重定向。请注意,这仅对单次重定向有效。

res = Net::HTTP.get_response URI res.header['location']

标头的 location 字段包含文件被重定向到的地址。

$ ./redirect.rb 
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>
::1 - - [03/Feb/2021:13:59:52 +0100] "GET /oldpage HTTP/1.1" 302 - 0.0012
::1 - - [03/Feb/2021:13:59:52 +0100] "GET /files/newpage.html HTTP/1.1" 200 112 0.0051

从日志中我们可以看到请求被重定向到了新的文件名。通信包括两个 GET 请求。

在本文中,我们已使用 Ruby net/http 模块。ZetCode 上还有类似的 Ruby HTTPClientRuby Faraday

作者

我叫 Jan Bodnar,是一名充满热情的程序员,拥有丰富的编程经验。我自 2007 年以来一直在撰写编程文章。迄今为止,我已撰写了 1,400 多篇文章和 8 本电子书。我在编程教学方面拥有十多年的经验。