# Ruby Faraday 教程

> 原文： [https://zetcode.com/web/rubyfaraday/](https://zetcode.com/web/rubyfaraday/)

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

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

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

```ruby
$ sudo gem install faraday

```

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

## Sinatra

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

```ruby
$ sudo gem install sinatra
$ sudo gem install thin

```

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

```ruby
$ pwd
/home/janbodnar/prog/sinatra/first
$ ls
main.rb

```

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

`main.rb`

```ruby
require 'sinatra'

get '/' do
    "First application"
end

```

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

```ruby
$ 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`命令启动应用。 瘦服务器启动； 它在 4567 端口上监听。

```ruby
$ curl localhost:4567/
First application

```

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

## 版本

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

`version.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

puts Faraday::VERSION
puts Faraday::default_adapter

```

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

```ruby
$ ./version.rb 
0.9.2
net_http

```

这是字符串的示例输出。

## 获取内容

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

`get_content.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

res = Faraday.get 'http://www.something.com' 

puts res.body

```

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

```ruby
$ ./get_content.rb 
<html><head><title>Something.</title></head>
<body>Something.</body>
</html>

```

这是`get_content.rb`脚本的输出。

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

`strip_tags.rb`

```ruby
#!/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 标签。

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

```

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

```ruby
$ ./strip_tags.rb 
Something.
Something.

```

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

## 状态

`Faraday::Response`的`status`方法返回响应的 HTTP 状态代码。

`status.rb`

```ruby
#!/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 请求，并检查返回状态。

```ruby
res = Faraday.get 'http://www.something.com'  
puts res.status

```

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

```ruby
puts res.success?

```

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

```ruby
$ ./status.rb 
200
true
404
false
302
false

```

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

## `head`方法

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

`head.rb`

```ruby
#!/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`网页的服务器，日期，上次修改时间，内容类型和内容长度。

```ruby
$ ./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 方法请求指定资源的表示形式。

`main.rb`

```ruby
require 'sinatra'

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

```

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

`mget.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

con = Faraday.new 

res = con.get 'http://localhost:4567/greet', { :name => 'Peter' }

puts res.body   

```

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

```ruby
$ ./mget.rb 
Hello Peter

```

这是示例的输出。

```ruby
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`

```ruby
#!/usr/bin/ruby

require 'faraday'

res = Faraday.get do |req|
    req.url 'http://localhost/greet' 
    req.params['name']  = 'Jan' 
end

puts res.body  

```

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

```ruby
$ ./mget2.rb 
Hello Peter

```

This is the output of the example.

## 用户代理

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

`main.rb`

```ruby
require 'sinatra'

get '/agent' do
    request.user_agent
end

```

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

`agent.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

con = Faraday.new 

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

puts res.body

```

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

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

```

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

```ruby
$ ./agent.rb 
Ruby script

```

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

## `post`方法

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

`main.rb`

```ruby
require 'sinatra'

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

```

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

`mpost.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

con = Faraday.new 'http://localhost'

res = con.post '/target', { :name => 'Jan' }

puts res.body

```

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

```ruby
$ ./mpost.rb 
Hello Jan

```

这是`mpost.rb`脚本的输出。

```ruby
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](http://www.dictionary.com) 上找到术语的定义。 要解析 HTML，我们使用`nokogiri`。 可以使用`sudo gem install nokogiri`命令安装。

`get_term.rb`

```ruby
#!/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`上找到了术语狗的定义。 `Nokogiri::HTML`用于解析 HTML 代码。

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

```

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

```ruby
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 对象表示法）是一种轻量级的数据交换格式。 人类很容易读写，机器也很容易解析和生成。

```ruby
$ sudo gem install json

```

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

`main.rb`

```ruby
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`

```ruby
#!/usr/bin/ruby

require 'faraday'
require 'json'

con = Faraday.new :url => 'http://localhost:4567/example.json'

res = con.get
data = JSON.parse res.body

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

```

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

```ruby
$ ./parse_json.rb 
Jane
17

```

This is the output of the example.

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

`main.rb`

```ruby
require 'sinatra'
require 'json'

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

```

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

`post_json.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

con = Faraday.new 

res = con.post do |req|
    req.url 'http://localhost:4567/readjson'
    req.headers['Content-Type'] = 'application/json'
    req.body = '{ "name": "Jane", "age": 17 }'
end

puts res.body

```

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

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

```

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

```ruby
$ ./post_json.rb 
Jane is 17 years old

```

This is the output of the example.

## 证书

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

```ruby
$ sudo gem install sinatra-basic-auth

```

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

`main.rb`

```ruby
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 应用中，我们指定授权逻辑并设置受保护的路由。

`credentials.rb`

```ruby
#!/usr/bin/ruby

require 'faraday'

con = Faraday.new :url => 'http://localhost/secure/'

user = 'user7'
passwd = '7user'

con.basic_auth  user, passwd
res = con.get 

puts res.body

```

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

```ruby
$ ./credentials.rb 
This is restricted area

```

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

## Faraday 中间件

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

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

### 重定向

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

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

```ruby
$ sudo gem install faraday_middleware

```

这些模块在`faraday_middleware`包中可用。

`main.rb`

```ruby
require 'sinatra'

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

```

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

`newpage.html`

```ruby
<!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`

```ruby
#!/usr/bin/ruby

require 'faraday'
require 'faraday_middleware'

con = Faraday.new 'http://localhost:4567/oldpage' do |con|
    con.use FaradayMiddleware::FollowRedirects, limit: 5
    con.adapter Faraday.default_adapter
end

res = con.get
puts res.body

```

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

```ruby
$ ./redirect.rb 
<!DOCTYPE html>
<html>
<head>
<title>New page</title>
</head>
<body>
<p>
This is a new page
</p>
</body>
</html>

```

This is the output of the example.

```ruby
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

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

`main.rb`

```ruby
require 'sinatra'

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

```

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

`logger.rb`

```ruby
#!/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 => "http://localhost:4567") do |build|
    build.request :url_encoded
    build.use MyLogger
    build.adapter  Faraday.default_adapter
end

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

```

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

```ruby
def call env
    on_request("request", env)
    @app.call(env).on_complete do
        on_response("response", env)
    end
end

```

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

```ruby
private
def on_request phase, env
    @logger.info("#{phase} : #{env.method} - #{env.url}") if env.method and env.url
end

```

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

```ruby
con = Faraday.new(:url => "http://localhost:4567") do |build|
    build.request :url_encoded
    build.use MyLogger
    build.adapter Faraday.default_adapter
end

```

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

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

```

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

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

```

This is the output of the example.

在本教程中，我们使用了 Ruby Faraday 模块。 在 ZetCode 上有类似的 [Ruby HTTPClient 教程](/web/rubyhttpclient/)和 [Ruby Net :: HTTP 教程](/web/rubynethttp/)。