Rails4內使用Grape套件取得資料庫的資料不像Rails3使用Grape一樣簡單,我覺得有點複雜。原本打算只用Rails4和Grape搭建API,後來覺得應該加入使用身份認證,所以決定使用Doorkeeper套件,當然要讓整體運作也用到其它套件,像是Devise,測試OAuth功能則使用OAuth2套件。紀錄搭建過程遇到的問題,提醒自己未來再搭建其它服務時可以根據這篇文章不用走重複的路。
在Grape已經使用Rails4的Model的前提下,Rails4必須加入controller元件與view的頁面,才會替換掉Rails4的預設歡迎頁面。我無法刪掉Rails4 app的預設歡迎頁面,因為它不像Rails3一樣,都會放在每個App內的資料夾。
透過URL使用在Rails內的Grape API,可以看另一篇文章。
使用Doorkeeper套件前,我建議先安裝並建立好Devise套件的身份認證機制,以及安裝OAuth2套件,OAuth2套件是一個OAuth client端的套件,可以協助我們測試OAuth功能是否完整。安裝Doorkeeper後,記得在config/initializers/doorkeeper.rb
檔內,修改resource_lwner_authenticator
部分,因為我讓Doorkeeper配合Devise,所以這部分我用兩行表示:
session[:user_return_to] = request.full_path
current_user || redirect_to(new_user_session_url)
接著在localhost:3000/oauth/applications
的網址,新增要測試OAuth client端名稱,我隨便取一個叫做curl test,Redirect URI則自行指定想要的網址,記得網址最後要加上斜線(Slash)才算完整,我卡在Redirect URI沒補上斜線很久。新增完成之後會回到Doorkeeper允許的oauth client頁面,點選剛才新增的client名稱,可以看到Client端的application id、secret和callback URL。(如果要用RSpec去測試授權OAuth client,則不適用此區塊的說明。)
20160304新增:
註:如果按了上圖中的 Authorize 按鈕,通常會出現 scope invalid, unknown 等錯誤畫面,此時只要加上瀏覽器的網址列最後加上 &scope=scopesyouwant,就會出現 Authorization code 的畫面。
第二步是開啟terminal程式,例如iterm,在Rails app家目錄下輸入 irb -r OAuth2
指令,進入載入了OAuth2套件的irb環境,這能夠協助我們得到Grant code和Access token。我們須輸入以下指令到irb內,這部分跟Railscast Pro #353影片一樣,你可以在Youtube看到那個影片:
callback = "yourredirecturi"
這字串最後一定要有斜線(Slash),是為了跟剛才在Doorkeeper網頁新增的Redirect URI相同。appid = "yourappid"
請按照剛才Doorkeeper圖片你新增所得到的application id。secret = "yourappsecret"
請按照剛才Doorkeeper圖片你新增所得到的secret。client = OAuth2::Client.new(appid, secret, site: "http://localhost:3000/")
建立一個OAuth clientclient.auth_code.authorize_url( redirect_uri: callback)
將appid、secret和callback的資料,整理成可向網站發送的OAuth URL query。將這點得到的網址貼到瀏覽器網址列,因為callback的網址目前沒有server端跑在後面,所以瀏覽器的畫面會是無法顯示網站內容之類的訊息,不過沒關係,我們只要取得網址列上的code參數值就好。access = client.auth_code.get_token('yourcodefromURLofBrowser', redirect_uri: callback)
這步驟會向網址發送request,並且得到結果,用第七行的指令會比較容易閱讀access_token。access.token
輸入這行會顯示容易閱讀版本的access_token,access_token將是你向API Server發送要求取得資料時的重要身份證明。做到這邊,可以準備向API server發送要求取得資料的訊息,不過我在這邊遇到了一個問題,那就是Rails在瀏覽器一直顯示No route match GET xxxxxx所以無法取得資料,可是我先前沒使用Doorkeeper時能夠成功取得資料,Debug好久才找到解決方法的方向,我再裝一個gem來確定我使用的Grape route是否有誤,這個套件是grape-rails-routes,它1.0的版本至少需要Rails4.1.4以上。裝完後,我用指令rails routes-with-grape列出所有API路由,發現自己發送的路由有誤,於是調整發送路由。調整路由之後,我發現API server偶爾可讓OAuth Client取得資料,只要Rails server重開後,常常又不能拿到API server的資料了,而同時Rails呈現資料的網頁界面則是正常運作。
附註:除了檢查route之外,我也檢查config/application.rb
中設定載入api的路徑是否正確,以及config/routes.rb
載入api的寫法與順序是否正確。
於是又遇到一個問題:如何讓API client每次都能取得資料。目前我嘗試出來的解決方式是:在terminal程式內,Rails App專案目錄下輸入rails server,輸入後,再到瀏覽器開啟Rails app專案的特定網址:localhost:3000/rails/info/routes_with_grape
,這樣做,OAuth API client端就可以正常取得想要的資料,而不會是No route match xxxxx了。 這個問題的發生原因目前我不了解,需要花時間研究發生原因以及如何完整地在正式環境(production environment)解決。
20160304新增:
使用RSpec去測試「使用者授權(Authorize) OAuth client」
前置工作
請在 Rails 專案底下的 spec 目錄之下再新增一個目錄: requests 。接著在 requests 目錄內,新增測試 api 的相關目錄,其完整目錄結構依此所述: spec/requests/api/v1/
。在目錄 v1 之下,新增測試 API 的 RSpec 檔案,例如是:specifiedvegetables_spec.rb
。另外要記得新增一個使用者在系統上,稍後的登入會用到。
測試檔案的內容
以 specifiedvegetables_spec.rb
舉例,這個測試內容會先登入系統,登入之後新增一個與使用者關聯的 OAuth client 資料到資料庫,接著取得 OAuth Authorize code,最後是刪除資料庫內有關這個 oauth client 的資料。以下是 specifiedvegetables_spec.rb
的部分檔案內容:
require 'rails_helper'
describe SpecifiedVegetables do
describe 'OAuth client require the grant' do
context 'When a Rest client send a request for the grant' do
before(:all) do # 在進行本情境的所有測試案例前,必須執行此區域的程式碼一次。此區域的程式碼只會執行一次。
post "http://localhost:3000/users/sign_in?user[email]=test%40adminabc%2Ecom&user[password]=12345678" # %40 是 @ 的 http url 轉碼, %2E 是 dot 的 http url 轉碼
# post 這行是送出登入所需訊息到系統,登入成功後系統通常會轉址到 root 頁面,所以要檢測是否登入成功,可使用程式碼: expect(response.status).to eq(302)
@app = Doorkeeper::Application.new :name => 'rspectest-101', :redirect_uri => 'https://localhost:3000/api/v1/specified_vegetables/', :scopes => 'public'
#這行是新增 oauth client (尚未存到資料庫),請自訂參數 :name, :redirect_uri 和 :scopes 的值。參數 :redirect_uri 必須使用 https 的網址,若使用 http 則 Doorkeeper 會反應必須是 http/SSL 的網址而顯示錯誤;建議 :scopes 至少指定一個值,用以規範 oauth client 能使用的 API 範圍。
@app.owner = User.last #例如指定成此系統最新的使用者 [email protected]。
@app.save # 使這個 oauth client 資料存到資料庫。
end
it 'should getting response code 302 for requesting authorization code' do
get "http://localhost:3000/oauth/authorize?response_type=code&client_id=" + @app.owner.oauth_applications.last.uid + "&redirect_uri=https%3A%2F%2Flocalhost%3A3000%2Fapi%2Fv1%2Fspecified%5Fvegetables%2F&scope=public"
# 適合用於開發和測試環境,這行是向 Doorkeeper 發出取得授權碼的要求。程式碼 @app.owner.oauth_applications.last.uid 無法縮減成 @app.uid ,若縮減則無法取得適當的 uid。redirect_uri 的值,其中符號必須依照 http url 轉換規則轉換,%3A 是冒號(:),%2F 是斜線(/),%5F是底線(_)。請勿忘記必須指定參數 scope 的值。
expect(response.status).to eq(302) #如果成功授權,則會取得 authorize code。
end
after(:all) do #執行這個情境的所有測試案例後,必須執行此區域的程式碼一次。此區域的程式碼只會執行一次。
@app.destroy # 清除這個 oauth client 在資料庫的內容。
end
end
end
end
在 terminal 程式(例如iterm2)中執行以下指令,必須在 Rails 專案的家目錄執行該指令,否則須自行引入相關檔案:
rspec spec/requests/api/v1/specifiedvegetables_spec.rb
使用RSpec後記:
rails console
指令,在此 console 環境先去驗證概念程式碼是否能在 RSpec 的測試檔案使用。
附註:
:redirect_uri
,請讓 config/initializers/doorkeeper.rb
內的 native_redirect_uri
設定成 nil ,即能接受 http 的 :redirect_uri
。本項設定預設是開啟。config/initializers/doorkeeper.rb
內有使用以下程式碼:skip_authorization do true do
20160424新增:
在Rails 4環境下,結合 Grape, Doorkeeper 和 Wine_bouncer gem 做出的API範例,請參閱本部落格的文章: Configurations and programming examples about Grape, Doorkeeper and Wine_bouncer gem in rails 4 app
參考資料: