原文: https://zetcode.com/web/rubyfaraday/

在本教程中,我们展示了如何使用 Ruby Faraday 模块。 我们获取数据,发布数据,使用 JSON 并连接到安全的网页。 我们还创建了一个自定义的 Faraday 中间件。 本教程使用 Sinatra 应用作为几个示例。 ZetCode 也有一个简洁的 Ruby 教程

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

Ruby Faraday是一个简单,灵活的 HTTP 客户端库,支持多个后端。 Faraday 也是一种中间件。

  1. $ sudo gem install faraday

该模块通过sudo gem install faraday命令安装。

Sinatra

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

  1. $ sudo gem install sinatra
  2. $ sudo gem install thin

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

  1. $ pwd
  2. /home/janbodnar/prog/sinatra/first
  3. $ ls
  4. main.rb

在第一个目录中,我们有一个main.rb文件,它是 Sinatra 应用文件。

main.rb

  1. require 'sinatra'
  2. get '/' do
  3. "First application"
  4. end

应用对/路由做出反应。 它将一条简单的消息发送回客户端。

  1. $ ruby main.rb
  2. == Sinatra (v1.4.7) has taken the stage on 4567 for development with backup from Thin
  3. Thin web server (v1.6.4 codename Gob Bluth)
  4. Maximum connections set to 1024
  5. Listening on localhost:4567, CTRL+C to stop

使用ruby main.rb命令启动应用。 瘦服务器启动; 它在 4567 端口上监听。

  1. $ curl localhost:4567/
  2. First application

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

版本

第一个 Faraday 程序将打印库和 Ruby 语言的版本。

version.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. puts Faraday::VERSION
  4. puts Faraday::default_adapter

这两个常数提供了库的版本号和默认的 Faraday 适配器。

  1. $ ./version.rb
  2. 0.9.2
  3. net_http

这是字符串的示例输出。

获取内容

get方法获取由给定 URL 标识的文档。

get_content.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. res = Faraday.get 'http://www.something.com'
  4. puts res.body

该脚本获取www.something.com网页的内容。

  1. $ ./get_content.rb
  2. <html><head><title>Something.</title></head>
  3. <body>Something.</body>
  4. </html>

这是get_content.rb脚本的输出。

以下程序获取一个小型网页,并剥离其 HTML 标签。

strip_tags.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday::Connection.new "http://www.something.com"
  4. res = con.get
  5. puts res.body.gsub(%r{</?[^>]+?>}, '')

该脚本会剥离www.something.com网页的 HTML 标签。

  1. puts res.body.gsub(%r{</?[^>]+?>}, '')

一个简单的正则表达式用于剥离 HTML 标记。

  1. $ ./strip_tags.rb
  2. Something.
  3. Something.

该脚本将打印网页的标题和内容。

状态

Faraday::Responsestatus方法返回响应的 HTTP 状态代码。

status.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. res = Faraday.get 'http://www.something.com'
  4. puts res.status
  5. puts res.success?
  6. res = Faraday.get 'http://www.something.com/news/'
  7. puts res.status
  8. puts res.success?
  9. res = Faraday.get 'http://www.urbandicionary.com/define.php?term=Dog'
  10. puts res.status
  11. puts res.success?

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

  1. res = Faraday.get 'http://www.something.com'
  2. puts res.status

使用status方法检查 HTTP 响应的状态。

  1. puts res.success?

success?方法指示状态代码是否成功。

  1. $ ./status.rb
  2. 200
  3. true
  4. 404
  5. false
  6. 302
  7. false

200 是对成功的 HTTP 请求的标准响应,404 指示找不到请求的资源,302 指示该资源已临时重定向。

head方法

head方法检索文档标题。 标头由字段组成,包括日期,服务器,内容类型或上次修改时间。

head.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new :url => "http://www.something.com"
  4. res = con.head
  5. puts res.headers['server']
  6. puts res.headers['date']
  7. puts res.headers['last-modified']
  8. puts res.headers['content-type']
  9. puts res.headers['content-length']

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

  1. $ ./head.rb
  2. Apache/2.4.12 (FreeBSD) OpenSSL/1.0.1l-freebsd mod_fastcgi/mod_fastcgi-SNAP-0910052141
  3. Tue, 10 May 2016 10:19:01 GMT
  4. Mon, 25 Oct 1999 15:36:02 GMT
  5. text/html
  6. 77

这是head.rb程序的输出。

get方法

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

main.rb

  1. require 'sinatra'
  2. get '/greet' do
  3. "Hello #{params[:name]}"
  4. end

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

mget.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new
  4. res = con.get 'http://localhost:4567/greet', { :name => 'Peter' }
  5. puts res.body

该脚本将具有值的变量发送到 Sinatra 应用。 该变量直接在 URL 中指定。

  1. $ ./mget.rb
  2. Hello Peter

这是示例的输出。

  1. 127.0.0.1 - - [10/May/2016:22:04:38 +0200] "GET /greet?name=Peter HTTP/1.1" 200 11 0.0034

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

get方法采用第二个参数,我们可以在其中指定查询参数。

mget2.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. res = Faraday.get do |req|
  4. req.url 'http://localhost/greet'
  5. req.params['name'] = 'Jan'
  6. end
  7. puts res.body

这是发出 GET 消息的另一种方式。

  1. $ ./mget2.rb
  2. Hello Peter

This is the output of the example.

用户代理

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

main.rb

  1. require 'sinatra'
  2. get '/agent' do
  3. request.user_agent
  4. end

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

agent.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new
  4. res = con.get do |req|
  5. req.url 'http://localhost:4567/agent'
  6. req.headers['User-Agent'] = 'Ruby script'
  7. end
  8. puts res.body

该脚本向 Sinatra 应用创建一个简单的 GET 请求。

  1. res = con.get do |req|
  2. req.url 'http://localhost:4567/agent'
  3. req.headers['User-Agent'] = 'Ruby script'
  4. end

用户代理在请求的headers属性中指定。

  1. $ ./agent.rb
  2. Ruby script

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

post方法

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

main.rb

  1. require 'sinatra'
  2. post '/target' do
  3. "Hello #{params[:name]}"
  4. end

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

mpost.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new 'http://localhost'
  4. res = con.post '/target', { :name => 'Jan' }
  5. puts res.body

脚本使用具有Jan值的name键发送请求。 POST 请求通过post方法发出。

  1. $ ./mpost.rb
  2. Hello Jan

这是mpost.rb脚本的输出。

  1. 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。 可以使用sudo gem install nokogiri命令安装。

get_term.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. require 'nokogiri'
  4. term = 'dog'
  5. con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term
  6. res = con.get
  7. doc = Nokogiri::HTML res.body
  8. doc.css("div.def-content").map do |node|
  9. s = node.text.strip!
  10. s.gsub!(/\s{3,}/, " ") unless (s == nil)
  11. puts s unless (s == nil)
  12. end

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

  1. con = Faraday.new :url => 'http://www.dictionary.com/browse/'+term

为了执行搜索,我们在 URL 的末尾附加了该词。

  1. doc = Nokogiri::HTML res.body
  2. doc.css("div.def-content").map do |node|
  3. s = node.text.strip!
  4. s.gsub!(/\s{3,}/, " ") unless (s == nil)
  5. puts s unless (s == nil)
  6. end

我们使用Nokogiri::HTML类解析内容。 定义位于<div class="def-content">标签内。 我们通过删除过多的空白来改善格式。

JSON 格式

JSON (JavaScript 对象表示法)是一种轻量级的数据交换格式。 人类很容易读写,机器也很容易解析和生成。

  1. $ sudo gem install json

如果以前没有安装过,则必须安装json包。

main.rb

  1. require 'sinatra'
  2. require 'json'
  3. get '/example.json' do
  4. content_type :json
  5. { :name => 'Jane', :age => 17 }.to_json
  6. end

Sinatra 应用发送 JSON 数据。 它使用to_json方法完成工作。

parse_json.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. require 'json'
  4. con = Faraday.new :url => 'http://localhost:4567/example.json'
  5. res = con.get
  6. data = JSON.parse res.body
  7. puts data["name"]
  8. puts data["age"]

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

  1. $ ./parse_json.rb
  2. Jane
  3. 17

This is the output of the example.

接下来,我们从 Ruby 脚本将 JSON 数据发送到 Sinatra 应用。

main.rb

  1. require 'sinatra'
  2. require 'json'
  3. post '/readjson' do
  4. data = JSON.parse request.body.read
  5. puts data
  6. "#{data["name"]} is #{data["age"]} years old"
  7. end

该应用读取 JSON 数据并发送回带有已解析值的消息。

post_json.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new
  4. res = con.post do |req|
  5. req.url 'http://localhost:4567/readjson'
  6. req.headers['Content-Type'] = 'application/json'
  7. req.body = '{ "name": "Jane", "age": 17 }'
  8. end
  9. puts res.body

该脚本将 JSON 数据发送到 Sinatra 应用并读取其响应。

  1. req.headers['Content-Type'] = 'application/json'

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

  1. $ ./post_json.rb
  2. Jane is 17 years old

This is the output of the example.

证书

basic_auth方法设置用于领域的名称和密码。 安全领域是一种用于保护 Web 应用资源的机制。

  1. $ sudo gem install sinatra-basic-auth

对于此示例,我们需要安装sinatra-basic-auth包。

main.rb

  1. require 'sinatra'
  2. require "sinatra/basic_auth"
  3. authorize do |username, password|
  4. username == "user7" && password == "7user"
  5. end
  6. get '/' do
  7. "hello"
  8. end
  9. protect do
  10. get "/secure" do
  11. "This is restricted area"
  12. end
  13. end

在 Sinatra 应用中,我们指定授权逻辑并设置受保护的路由。

credentials.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. con = Faraday.new :url => 'http://localhost/secure/'
  4. user = 'user7'
  5. passwd = '7user'
  6. con.basic_auth user, passwd
  7. res = con.get
  8. puts res.body

该脚本连接到安全网页; 它提供访问该页面所需的用户名和密码。

  1. $ ./credentials.rb
  2. This is restricted area

使用正确的凭据,credentials.rb脚本返回受限制的数据。

Faraday 中间件

中间件是连接两个原本独立的应用的软件。 除了作为 HTTP 客户端之外,Faraday 还充当中间件。 这个概念与 Ruby Rack 非常相似。

Faraday::Connection包含一个中间件列表。 Faraday 中间件传递有请求和响应信息的env哈希。 中间件可以在执行请求之前和之后操纵此信息。

重定向

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

重定向是在 Faraday 中间件模块之一中实现的。

  1. $ sudo gem install faraday_middleware

这些模块在faraday_middleware包中可用。

main.rb

  1. require 'sinatra'
  2. get "/oldpage" do
  3. redirect to("/files/newpage.html"), 302
  4. end

在 Sinatra 应用中,我们使用redirect命令重定向到其他位置。

newpage.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <title>New page</title>
  5. </head>
  6. <body>
  7. <p>
  8. This is a new page
  9. </p>
  10. </body>
  11. </html>

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

redirect.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. require 'faraday_middleware'
  4. con = Faraday.new 'http://localhost:4567/oldpage' do |con|
  5. con.use FaradayMiddleware::FollowRedirects, limit: 5
  6. con.adapter Faraday.default_adapter
  7. end
  8. res = con.get
  9. puts res.body

该脚本访问旧页面并遵循重定向。

  1. $ ./redirect.rb
  2. <!DOCTYPE html>
  3. <html>
  4. <head>
  5. <title>New page</title>
  6. </head>
  7. <body>
  8. <p>
  9. This is a new page
  10. </p>
  11. </body>
  12. </html>

This is the output of the example.

  1. 127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /oldpage HTTP/1.1" 302 - 0.0199
  2. 127.0.0.1 - - [10/May/2016:22:14:16 +0200] "GET /files/newpage.html HTTP/1.1" 200 113 0.0073

从日志中,我们可以看到该请求已重定向到新的文件名。 通信包含两个 GET 消息。

MyLogger

在以下示例中,我们创建了自己的小型中间件。 它实现了请求和响应日志记录。

main.rb

  1. require 'sinatra'
  2. get '/greet' do
  3. "Hello #{params[:name]}"
  4. end

这是一个 Sinatra 应用,它向客户端发送问候语。

logger.rb

  1. #!/usr/bin/ruby
  2. require 'faraday'
  3. require 'logger'
  4. class MyLogger
  5. def initialize app
  6. @app = app
  7. @logger = Logger.new(STDOUT)
  8. end
  9. def call env
  10. on_request("request", env)
  11. @app.call(env).on_complete do
  12. on_response("response", env)
  13. end
  14. end
  15. private
  16. def on_request phase, env
  17. @logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
  18. end
  19. private
  20. def on_response phase, env
  21. @logger.info("#{phase} : #{env.body}") if env.body
  22. end
  23. end
  24. con = Faraday.new(:url => "http://localhost:4567") do |build|
  25. build.request :url_encoded
  26. build.use MyLogger
  27. build.adapter Faraday.default_adapter
  28. end
  29. res = con.get "/greet", {'name' => 'Jan'}

在这里,我们创建一个中间件,该中间件实现了对控制台的日志记录。

  1. def call env
  2. on_request("request", env)
  3. @app.call(env).on_complete do
  4. on_response("response", env)
  5. end
  6. end

中间件必须实现call方法。 它执行用于请求和响应的方法。

  1. private
  2. def on_request phase, env
  3. @logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
  4. end

生成请求后,将调用on_request方法。 该方法记录阶段,请求方法和 URL。

  1. con = Faraday.new(:url => "http://localhost:4567") do |build|
  2. build.request :url_encoded
  3. build.use MyLogger
  4. build.adapter Faraday.default_adapter
  5. end

MyLogger中间件通过use方法添加到栈中。 当连接对象执行请求时,它将创建一个共享的env哈希,将外部中间件包装在每个内部中间件周围,并执行call方法。

  1. res = con.get "/greet", {'name' => 'Jan'}

一条消息发送到 Sinatra 应用。 该请求和响应被记录到终端。

  1. $ ./logger.rb
  2. I, [2016-05-11T14:48:55.700198 #4945] INFO -- : request : get - http://localhost:4567/greet?name=Jan
  3. I, [2016-05-11T14:48:55.706989 #4945] INFO -- : response : Hello Jan

This is the output of the example.

在本教程中,我们使用了 Ruby Faraday 模块。 在 ZetCode 上有类似的 Ruby HTTPClient 教程Ruby Net :: HTTP 教程