Link Search Menu Expand Document

2. 串接第三方 API 服務

2-1 目標

上一章我們已經可以用 Ruby 程序抓到資料了,這一章我們將整合進 Rails,將抓到的資料存進資料庫,並且可以更新天氣資訊。

  1. 建立一個 City model,然後把 API 抓取回來的城市資料存進資料庫
  2. 新增 cities controller 和清單頁面,讓用戶可以瀏覽城市資料
  3. 用戶可以更新指定城市的氣溫資訊

2-2 初始專案,建立 City Model

在 Terminal 下輸入:

 rails new api_exercise
 cd api_exercise
 git init

編輯 Gemfile 加上 gem 'rest-client',然後執行 bundle

執行 rails g model city

編輯 city 的 migration 檔案 db/migrate/201703XXXXXXXX_create_cities.rb

 class CreateCities < ActiveRecord::Migration[5.0]
   def change
     create_table :cities do |t|
+      t.string :juhe_id
+      t.string :province
+      t.string :city
+      t.string :district
+      t.string :current_temp
       t.timestamps
     end

+    add_index :cities, :juhe_id
   end
 end

接著執行 rake db:migrate 建立資料庫 table。

2-3 抓取城市資料儲存下來

新增 lib/tasks/dev.rake,放在這個目錄下的 rake 檔案是用來編寫任務腳本,讓我們在 Terminal 中可以執行它:

namespace :dev do
  task :fetch_city => :environment do
    puts "Fetch city data..."
    response = RestClient.get "http://v.juhe.cn/weather/citys", :params => { :key => "你申請的key放這裡" }
    data = JSON.parse(response.body)

    data["result"].each do |c|
      existing_city = City.find_by_juhe_id( c["id"] )
      if existing_city.nil?
        City.create!( :juhe_id => c["id"], :province => c["province"],
                      :city => c["city"], :district => c["district"] )
      end
    end

    puts "Total: #{City.count} cities"
  end
end

執行 bundle exec rake dev:fetch_city 就會執行這個任務,把 2574 筆城市存進資料庫。

juhe_id 這個字段的目的是存下第三方那邊的 id,這樣我們之後在更新數據的時候,就可以進行比對、避免重復新增。

2-4 在畫面上顯示出來

編輯 config/routes.rb 新增一行

   Rails.application.routes.draw do
+   resources :cities
   end

執行 rails g controller cities

編輯 app/controllers/cities_controller.rb

 class CitiesController < ApplicationController

+  def index
+    @cities = City.all
+  end

 end

新增 app/views/cities/index.html.erb

<table class="table">
<tr>
  <th>Juhe ID</th>
  <th>Province</th>
  <th>City</th>
  <th>District</th>
  <th>Temp</th>
</tr>
<% @cities.each do |city| %>
  <tr>
    <td><%= city.juhe_id %></td>
    <td><%= city.province %></td>
    <td><%= city.city %></td>
    <td><%= city.district %></td>
    <td></td>
  </tr>
<% end %>
</table>

啟動伺服器 rails s,打開瀏覽器 http://localhost:3000/cities 就會看到城市資料了。

city

這裡省略了安裝 Bootstrap 的步驟,如果沒安裝也沒關系,畫面會有差異而已。

2-5 更新城市天氣

我們希望存下來當前溫度。根據 文檔 的說明,可以找到氣溫的 API 說明。

首先修改 config/routes.rb,新增一個操作:

-  resources :cities
+  resources :cities do
+    member do
+      post :update_temp
+    end
+  end

在畫面上放一個按鈕,編輯 app/views/cities/index.html

- <td></td>
+ <td>
+   <%= city.current_temp %>
+   <%= link_to "更新溫度", update_temp_city_path(city), :method => :post %>
+ </td>

新增一個 action,編輯 app/controller/cities_controller.rb

def update_temp
  city = City.find(params[:id])

    response = RestClient.get "http://v.juhe.cn/weather/index",
                              :params => { :cityname => city.juhe_id, :key => "你申請的key放這裡" }
    data = JSON.parse(response.body)

    city.update( :current_temp => data["result"]["sk"]["temp"] )

    redirect_to cities_path
end

這樣點擊「更新溫度」後,就會更新氣溫了。

2-6 保護 API Key

在串接第三方應用時,第三方的 API Key 我們不希望寫死在程式碼裡面,一來是因為我們不想把這些敏感的 keys 放到版本控制系統裡面。二來是因為將來佈署的時候,在 production 環境下,api key 會另外申請一個不一樣,因此我們希望容易抽換。

新增 config/juhe.yml 作為設定檔,內容如下:

development:
  api_key: "你申請的key放這裡"
production:
  api_key: "之後佈署上production的話,key放這裡"

編輯 config/application.rb,在最下麵插入一行:

# (略)

JUHE_CONFIG = Rails.application.config_for(:juhe)

編輯 app/controller/cities_controller.rblib/tasks/dev.rake,把 "你申請的key放這裡" 置換成 JUHE_CONFIG["api_key"] 即可。

要注意:

  • YAML 格式使用空白縮排來表達資料的階層關系,請務必縮排整齊
  • YAML 格式會區分數字和字串,例如 01234 會看成 1234,如果要確保被解析成字串,請加上引號,例如"01234"
  • 讀出來的 Hash 是用字串 key,不是 symbol key。是 JUHE_CONFIG["api_key"]而不是 JUHE_CONFIG[:api_key]

接著我們要告訴 Git 不要 commit 這個檔案,這樣就不用擔心 git push 會把 api key 洩漏出去。

編輯 .gitignore,插入一行

config/juhe.yml

依照慣例,你可以複製一個 juhe.yml.example 檔案放進版本控制系統裡面,這可以給你同事當作範例參考,內容例如:

development:
  api_key: "<juhe api key>"

補充參考

可以從以下的 API 服務商中找你有興趣的資料集:


Copyright © 2010-2021 Wen-Tien Chang All Rights Reserved.