Ruby Faraday 教程
最后修改于 2023 年 1 月 10 日
在本教程中,我们将介绍如何使用 Ruby Faraday 模块。我们将获取数据、发布数据、处理 JSON,并连接到安全的网页。我们还将创建一个自定义的 Faraday 中间件。本教程使用 Sinatra 应用程序进行了一些示例。ZetCode 还有一个简洁的Ruby 教程。
超文本传输协议 (HTTP) 是一种用于分布式、协作式、超媒体信息系统的应用协议。HTTP 是万维网数据通信的基础。
Ruby Faraday 是一个简单、灵活的 HTTP 客户端库,支持多种后端。Faraday 也是一个中间件。
$ sudo gem install faraday
该模块使用 sudo gem install faraday 命令安装。
Sinatra
Sinatra 是一个流行的 Ruby Web 应用程序框架。它易于安装和设置。我们的一些示例也将使用 Sinatra 应用程序。
$ sudo gem install sinatra $ sudo gem install thin
我们安装 Sinatra 和 Thin Web 服务器。如果安装了 Thin,Sinatra 会自动选择 Thin 而不是默认的 WEBrick 服务器。
$ pwd /home/janbodnar/prog/sinatra/first $ ls main.rb
在第一个目录中,我们有一个 main.rb 文件,它是 Sinatra 应用程序文件。
require 'sinatra'
get '/' do
"First application"
end
该应用程序响应 / 路由。它将一个简单的消息发送回客户端。
$ ruby main.rb == Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Thin Thin web server (v1.6.4 codename Gob Bluth) Maximum connections set to 1024 Listening on localhost:4567, CTRL+C to stop
应用程序通过 ruby main.rb 命令启动。Thin 服务器将被启动;它监听 4567 端口。
$ curl localhost:4567/ First application
使用 curl 命令行工具,我们连接到服务器并访问 / 路由。控制台会显示一条消息。
版本
第一个 Faraday 程序打印库和 Ruby 语言的版本。
#!/usr/bin/ruby require 'faraday' puts Faraday::VERSION puts Faraday::default_adapter
这两个常量提供了库版本号和默认的 Faraday 适配器。
$ ./version.rb 0.9.2 net_http
这是字符串的示例输出。
获取内容
get 方法获取由给定 URL 标识的文档。
#!/usr/bin/ruby require 'faraday' res = Faraday.get 'http://www.something.com' puts res.body
该脚本抓取 www.something.com 网页的内容。
$ ./get_content.rb <html><head><title>Something.</title></head> <body>Something.</body> </html>
这是 get_content.rb 脚本的输出。
以下程序获取一个小型网页并去除其 HTML 标签。
#!/usr/bin/ruby
require 'faraday'
con = Faraday::Connection.new "http://www.something.com"
res = con.get
puts res.body.gsub(%r{</?[^>]+?>}, '')
该脚本剥离了 www.something.com 网页的 HTML 标签。
puts res.body.gsub(%r{</?[^>]+?>}, '')
使用一个简单的正则表达式来去除 HTML 标签。
$ ./strip_tags.rb Something. Something.
该脚本打印网页的标题和内容。
状态
Faraday::Response 的 status 方法返回响应的 HTTP 状态码。
#!/usr/bin/ruby require 'faraday' res = Faraday.get 'http://www.something.com' puts res.status puts res.success? res = Faraday.get 'http://www.something.com/news/' puts res.status puts res.success? res = Faraday.get 'http://www.urbandicionary.com/define.php?term=Dog' puts res.status puts res.success?
我们使用 get 方法执行三个 HTTP 请求,并检查返回的状态。
res = Faraday.get 'http://www.something.com' puts res.status
HTTP 响应的状态通过 status 方法进行检查。
puts res.success?
success? 方法指示状态码是否成功。
$ ./status.rb 200 true 404 false 302 false
200 是成功 HTTP 请求的标准响应,404 表示找不到请求的资源,302 表示资源被暂时重定向。
head 方法
head 方法检索文档头信息。头信息由字段组成,包括日期、服务器、内容类型或最后修改时间。
#!/usr/bin/ruby require 'faraday' con = Faraday.new :url => "http://www.something.com" res = con.head puts res.headers['server'] puts res.headers['date'] puts res.headers['last-modified'] puts res.headers['content-type'] puts res.headers['content-length']
该示例打印 www.something.com 网页的服务器、日期、最后修改时间、内容类型和内容长度。
$ ./head.rb Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141 Tue, 10 May 2016 10:19:01 GMT Mon, 25 Oct 1999 15:36:02 GMT text/html 77
这是 head.rb 程序的输出。
get 方法
get 方法向服务器发出 GET 请求。GET 方法请求指定资源的表示形式。
require 'sinatra'
get '/greet' do
"Hello #{params[:name]}"
end
这是 Sinatra 应用程序文件。在收到 /greet 路由后,它会返回一条包含客户端发送的名称的消息。
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.get 'https://:4567/greet', { :name => 'Peter' }
puts res.body
该脚本将一个带有值的变量发送到 Sinatra 应用程序。该变量直接在 URL 中指定。
$ ./mget.rb Hello Peter
这是示例的输出。
127.0.0.1 - - [10/May/2016:22:04:38 +0200] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0034
在 Thin 服务器的此日志中,我们可以看到参数已被编码到 URL 中。
get 方法接受第二个参数,我们可以在其中指定查询参数。
#!/usr/bin/ruby
require 'faraday'
res = Faraday.get do |req|
req.url 'https:///greet'
req.params['name'] = 'Jan'
end
puts res.body
这是发出 GET 消息的另一种方式。
$ ./mget2.rb Hello Peter
这是示例的输出。
用户代理
在本节中,我们指定了用户代理的名称。
require 'sinatra'
get '/agent' do
request.user_agent
end
Sinatra 应用程序返回客户端发送的用户代理。
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.get do |req|
req.url 'https://:4567/agent'
req.headers['User-Agent'] = 'Ruby script'
end
puts res.body
此脚本向 Sinatra 应用程序创建了一个简单的 GET 请求。
res = con.get do |req|
req.url 'https://:4567/agent'
req.headers['User-Agent'] = 'Ruby script'
end
用户代理在请求的 headers 属性中指定。
$ ./agent.rb Ruby script
服务器响应了我们随请求发送的代理名称。
发布一个值
post 方法在给定的 URL 上分派一个 POST 请求,为表单填写内容提供键/值对。
require 'sinatra'
post '/target' do
"Hello #{params[:name]}"
end
Sinatra 应用程序在 /target 路由上返回问候语。它从 params 哈希中获取值。
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new 'https://'
res = con.post '/target', { :name => 'Jan' }
puts res.body
该脚本发送一个带有 name 键、值为 Jan 的请求。POST 请求使用 post 方法发出。
$ ./mpost.rb Hello Jan
这是 mpost.rb 脚本的输出。
127.0.0.1 - - [11/May/2016:13:49:44 +0200] "POST /target HTTP/1.1" 200 9 0.0006
使用 POST 方法时,值不会发送在请求 URL 中。
从字典中检索定义
在接下来的示例中,我们在 www.dictionary.com 上查找一个术语的定义。为了解析 HTML,我们使用了 nokogiri gem。可以使用 sudo gem install nokogiri 命令安装它。
#!/usr/bin/ruby
require 'faraday'
require 'nokogiri'
term = 'dog'
con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term
res = con.get
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
在此脚本中,我们在 www.dictionary.com 上查找术语 dog 的定义。Nokogiri::HTML 用于解析 HTML 代码。
con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term
要执行搜索,我们将术语附加到 URL 的末尾。
doc = Nokogiri::HTML res.body
doc.css("div.def-content").map do |node|
s = node.text.strip!
s.gsub!(/\s{3,}/, " ") unless (s == nil)
puts s unless (s == nil)
end
我们使用 Nokogiri::HTML 类解析内容。定义位于 <div class="def-content"> 标签内。我们通过删除多余的空白来改进格式。
JSON
JSON (JavaScript Object Notation) 是一种轻量级的数据交换格式。它易于人类阅读和编写,也易于机器解析和生成。
$ sudo gem install json
如果我们之前没有安装 json gem,我们需要安装它。
require 'sinatra'
require 'json'
get '/example.json' do
content_type :json
{ :name => 'Jane', :age => 17 }.to_json
end
Sinatra 应用程序发送 JSON 数据。它使用 to_json 方法来完成此操作。
#!/usr/bin/ruby require 'faraday' require 'json' con = Faraday.new :url => 'https://:4567/example.json' res = con.get data = JSON.parse res.body puts data["name"] puts data["age"]
该示例读取 Sinatra 应用程序发送的 JSON 数据。
$ ./parse_json.rb Jane 17
这是示例的输出。
接下来,我们从 Ruby 脚本向 Sinatra 应用程序发送 JSON 数据。
require 'sinatra'
require 'json'
post '/readjson' do
data = JSON.parse request.body.read
puts data
"#{data["name"]} is #{data["age"]} years old"
end
此应用程序读取 JSON 数据并发送回包含解析值的消息。
#!/usr/bin/ruby
require 'faraday'
con = Faraday.new
res = con.post do |req|
req.url 'https://:4567/readjson'
req.headers['Content-Type'] = 'application/json'
req.body = '{ "name": "Jane", "age": 17 }'
end
puts res.body
此脚本向 Sinatra 应用程序发送 JSON 数据并读取其响应。
req.headers['Content-Type'] = 'application/json'
请求中必须指定 'application/json' 内容类型。
$ ./post_json.rb Jane is 17 years old
这是示例的输出。
凭据
basic_auth 方法设置用于域的用户名和密码。安全域是用于保护 Web 应用程序资源的机制。
$ sudo gem install sinatra-basic-auth
对于此示例,我们需要安装 sinatra-basic-auth gem。
require 'sinatra'
require "sinatra/basic_auth"
authorize do |username, password|
username == "user7" && password == "7user"
end
get '/' do
"hello"
end
protect do
get "/secure" do
"This is restricted area"
end
end
在 Sinatra 应用程序中,我们指定了授权逻辑并设置了一个受保护的路由。
#!/usr/bin/ruby require 'faraday' con = Faraday.new :url => 'https:///secure/' user = 'user7' passwd = '7user' con.basic_auth user, passwd res = con.get puts res.body
该脚本连接到受保护的网页;它提供访问该页面所需的用户名和密码。
$ ./credentials.rb This is restricted area
使用正确的凭据,credentials.rb 脚本将返回受限制的数据。
Faraday 中间件
中间件 是连接两个独立应用程序的软件。除了作为 HTTP 客户端,Faraday 还充当中介。这个概念与 Ruby Rack 非常相似。
Faraday::Connection 包含一个中间件列表。Faraday 中间件接收一个 env 哈希,其中包含请求和响应信息。中间件可以在请求执行之前和之后操纵这些信息。
重定向
重定向是将一个 URL 转发到另一个 URL 的过程。HTTP 响应状态码 302 用于临时 URL 重定向。
重定向实现在 Faraday 的一个中间件模块中。
$ sudo gem install faraday_middleware
这些模块在 faraday_middleware gem 中可用。
require 'sinatra'
get "/oldpage" do
redirect to("/files/newpage.html"), 302
end
在 Sinatra 应用程序中,我们使用 redirect 命令重定向到另一个位置。
<!DOCTYPE html> <html> <head> <title>New page</title> </head> <body> <p> This is a new page </p> </body> </html>
这是位于 public/files 子目录中的 newpage.html 文件。
#!/usr/bin/ruby
require 'faraday'
require 'faraday_middleware'
con = Faraday.new 'https://:4567/oldpage' do |con|
con.use FaradayMiddleware::FollowRedirects, limit: 5
con.adapter Faraday.default_adapter
end
res = con.get
puts res.body
此脚本访问旧页面并跟随重定向。
$ ./redirect.rb <!DOCTYPE html> <html> <head> <title>New page</title> </head> <body> <p> This is a new page </p> </body> </html>
这是示例的输出。
127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /oldpage HTTP/1.1" 302 - 0.0199 127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /files/newpage.html HTTP/1.1" 200 113 0.0073
从日志中我们可以看到请求被重定向到了新的文件名。通信包括两个 GET 请求。
MyLogger
在接下来的示例中,我们将创建自己的小型中间件。它实现了请求和响应日志记录。
require 'sinatra'
get '/greet' do
"Hello #{params[:name]}"
end
这是一个 Sinatra 应用程序,它向客户端发送问候语。
#!/usr/bin/ruby
require 'faraday'
require 'logger'
class MyLogger
def initialize app
@app = app
@logger = Logger.new(STDOUT)
end
def call env
on_request("request", env)
@app.call(env).on_complete do
on_response("response", env)
end
end
private
def on_request phase, env
@logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
end
private
def on_response phase, env
@logger.info("#{phase} : #{env.body}") if env.body
end
end
con = Faraday.new(:url => "https://:4567") do |build|
build.request :url_encoded
build.use MyLogger
build.adapter Faraday.default_adapter
end
res = con.get "/greet", {'name' => 'Jan'}
这里我们创建一个实现了控制台日志记录的中间件。
def call env
on_request("request", env)
@app.call(env).on_complete do
on_response("response", env)
end
end
中间件必须实现 call 方法。它为请求和响应执行一个方法。
private
def on_request phase, env
@logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
end
在生成请求时,会调用 on_request 方法。该方法记录阶段、请求方法和 URL。
con = Faraday.new(:url => "https://:4567") do |build|
build.request :url_encoded
build.use MyLogger
build.adapter Faraday.default_adapter
end
MyLogger 中间件使用 use 方法添加到堆栈中。当连接对象执行请求时,它会创建一个共享的 env 哈希,将外部中间件包装在每个内部中间件周围,并执行 call 方法。
res = con.get "/greet", {'name' => 'Jan'}
消息被发送到 Sinatra 应用程序。请求和响应被记录到终端。
$ ./logger.rb I, [2016-05-11T14:48:55.700198 #4945] INFO -- : request : get - https://:4567/greet?name=Jan I, [2016-05-11T14:48:55.706989 #4945] INFO -- : response : Hello Jan
这是示例的输出。
在本教程中,我们学习了 Ruby Faraday 模块。ZetCode 上也有类似的Ruby HTTPClient 教程和Ruby Net::HTTP 教程。