全--教程API, gem 'rest-client'(用於發簡單請求); 請求測試;

安裝:rest-client4400✨
gem install rest-client

 一個簡單的HTTP和REST client for Ruby.javascript

 能夠用它來發HTTP請求css

基本用法:html

require 'rest-client' java

RestClient.get(url, headers={})git

RestClient.post(url, payload, headers={}) github

 


什麼是API

Application Programming Interface: 程序和程序的接口,定義接口叫什麼名字,要傳什麼參數進去,它會回傳什麼東西出來,可能回發生的errors等等。web

 

在寫Ruby程序的時候,使用library庫的方法,這時候API就是method的名字,參數,回傳值等等。 數據庫

對Web應用來講,API就是在定義網址URL的樣子,請求的HTTP方法是什麼,傳什麼參數過去,返回什麼資料格式。json

經常使用的就是JSON, XML。api


 

 

JSON格式的轉化,to_json和parse 

 

> require 'json'

 => true
> { :id => 123, :name => "foobar" } .to_json
 => "{\"id\":123,\"name\":\"foobar\"}"
> JSON.parse( "{\"id\":123,\"name\":\"foobar\"}" )
 => {"id"=>123, "name"=>"foobar"}

 

 使用聚合數據網

 

  1. 註冊聚合數據網,選擇一類數據並自動獲得它的appAPI:
  2. 登錄這個api的網站https://www.juhe.cn/docs/api/id/39 ,下載示例代碼。
  3. 使用請求示例:http://v.juhe.cn/weather/citys?key=您申請的KEY
  4. irb

 

require 'rest-client'
require 'json'
response = RestClient.get "http://v.juhe.cn/weather/citys?key=您申請的KEY
"
data = JSON.parse(response.body)#獲得hash格式的數據集合
data.keys#獲得keys
data["result"][0]#獲得具體數據。

 


 

bundle exec

在當前的bundle中,執行一個script 

 


Rake(software)  維基百科

rake是一個軟件task管理和bulid automation tool.It allows the user to specify tasks and describe dependencies as well as to group tasks in a namespace.
 

 

https://ruby.github.io/rake/

⚠️ruby新版已經放棄使用rake了,這裏就不學習了。

 


 


 

在「 離線保存的全棧文件/個人練習/webapi練習/api_exercise」目錄創建本教程app.

 

在rails上得到聚合網的天氣API, 

 

重點1:創建lib/tasks/dev.rake文件,創建一個script

# 編寫任務script 執行bundle exec rake dev:fetch_city
namespace :dev do
  task :fetch_city => :environment do
    puts "Fetch city data..."
    # 從聚合數據網上,獲得api鑰匙,而後下載json格式的數據。
    response = RestClient.get "http://v.juhe.cn/weather/citys?key=214b163d003a4799cb76359b6d30b7de"
    # 轉化爲hash格式
    data = JSON.parse(response.body)
    # 把數據存入建立的City數據庫中。
    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
 

重點2: 保護API key

 

  1. 新增config/juhe.yml , 設置development環境和production環境的api_key:"XXX" 
  2. 而後在application.rb中的Applicaiton類中設置一個常量JUHE_CONFIG,用於存儲不一樣程序環境下的api_key。
  3. JUHE_CONFIG = Rails.application.config_for(:juhe)
  4. 把程序中的api_key換成 JUHE_CONFIG["api_key"]
  5. 把juhe.yml標記在.gitignorre中: /config/juhe.yml, 不進行版本控制,保護密匙。
  6. 慣例新增一個juhe.yml.example,讓同事看到這個樣例。

 

⚠️ YAML格式是縮進的。區分數字和字符串,hash的key用字串表示。

 

 


 

根據火車train 的API,創建查詢和訂票系統:

 

要實做一個訂票系統 API 服務器,能夠提供給手機 iOS, Android 應用程式,或是一個開放平臺給別的開發者串接使用。

重點1:

routes.rb的設置:

  namespace :api, :default => {:format => :json} do
    namespace :v1 do
      get "/trains" => 'trains#index', :as => :trains
      get "/trains/:train_number" => 'trains#show', :as => :train

 

as用來產生路由地址的方法

api_v1_trains_url

會請求http://localhost:4000/api/v1/trains 這個網頁。

api_vi_train_url(train.number) 

會請求轉到http://localhost:4000/api/v1/trains/0822 ,假設train.number是「0822」

 

 

重點2:

render :json => {變量} 

會把變量轉成 JSON 字串輸出。這裏不須要準備 View .erb 檔案。
由於這兩個 API 都是用 HTTP GET 讀取,咱們能夠直接打開瀏覽器,瀏覽 http://localhost:3000/api/v1/trains 就是用 GET 讀取資料

 

例子:

在reservations_controller.rb#create方法中:

if @reservation.save

  render :json => {...}

else

  render :json => {:message => "訂票失敗", :errors => @reservation.errors }, status =>400 

 

重點3:

--no--assets選項的意思,不生成相關javascritps和styleshees的對應文件。 

rails g controller api::v1::reservations --no-assets

不生產:

 invoke  assets
      invoke    coffee
      create      app/assets/javascripts/api/v1/reservations.coffee
      invoke    scss
      create      app/assets/stylesheets/api/v1/reservations.scss

 


❌1

報告錯誤, InvalidAuthenticity 指未經過真實性驗證。

ActionController::InvalidAuthenticityToken in Api::V1::ReservationsController#create

 

在create方法中,須要驗證validations。猜想多是驗證的問題。

打開rails console,輸入Reservation.count , 提示錯誤❌:

ArgumentError (Unknown validator: 'ScopeValidator') 

發現驗證格式寫錯誤了:

❌validates :seat_number, :scope => :train_id , uniqueness: true ❌

✅validates :seat_number, uniqueness: {:scope => :train_id}

或 validates_uniqueness_of :seat_number, :scope => :train_id 

 

但仍然未解決第一個❌:不過不影響在控制檯,模擬create方法,✅生產reservation記錄。

另外,destroy,update都會報告相似❌。 

 

✅因而複製問題到谷歌和stackoverflow, 找到完美問題緣由: 

解決辦法:在api控制器上加上 skip_before_action :verify_authenticity_token

 

我已經讓ApiController直接繼承ActionController::Base 

class ApiController < ActionController::Base

但仍是不能逃脫檢查 僞信息和敏感請求參數。

 

Rails API 

有一個模塊RequestForgeryProtection,內有2個類方法:

第一個: 

class ApplicationController < ActionController::Base
  protect_from_forgery
end

 ⚠️,GET和HEAD request不會被檢查

 ⚠️,有vaild options:

:only/:except

:if/:unless 

:with => :null_session/:reset_session/:exception 

 

第二個:

關掉虛假信息的請求保護

 skip_before_action :verify_authenticity_token

  


 

 

train.reservations.pluck(:seat_number) 

獲得關聯對象集合的某個屬性的集合: 至關於查詢method

Reservation.joins(:trains).where("trains.id == *** ").select(:seat_number)

或者 純SQL

select reservations.seat_number FROM reservations inner JOIN trains ON  trains.id == reservations.train_id where train.id == **

 


 

給火車訂票系統,添加用戶,用戶能夠訂票 

 

重點1

給user增長一個API key ,做爲惟一識別碼。

    add_column :users, :authenication_token, :string

    add_index :users, :authenication_token, :unique => true

在user.rb中,添加self.authentication_token = Devise.friendly_token 

一個隨機的20個字母的字符串 (見gem 文檔)

 

惟一的識別碼, 

  • 安全性,亂數產生的強度比密碼高,甚至能夠設計有效時間
  • 獨立性,使用者改密碼不會影響 api key,這樣客戶端就不須要從新設定過

 

如何寫用戶認證API:

https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example (已過時,有可選的連接)

可選: 

devise_token_auth(2450✨) 2018-7有更新

 Token ,基於Rails JSON APIs的驗證的token。

能夠和devise一塊兒用,支持使用Devise進行email驗證,以及用戶註冊,登入,密碼相關修改。 

  

 


 

 

request.format = :json

 

增長一個views/api/v1/trains/show.json.jbuilder

在controllers/api/v1/trains_controller.rb中增長show方法。 

 

⚠️寫網址的時候務必加上.json

http://localhost:4000/api/v1/trains/0603.json

不然沒法找到show.json.jbuilder模版,由於默認是text/html。

或者在controller中加上:

  # before_action :set_default_format
  #
  # def set_default_format
  #   request.format = :json
  # end

 

 


 

 

分析:get "/trains/:train_number" => 'trains#show', :as => :train 

 

 /trains/:train_number 是指URL, 相似user/:id

⚠️加上冒號:,是指具體的記錄的屬性名稱。 

 as: :train是用於生成helper連接 ,train_url(train.number)

 

 


 

 

分頁功能: 

加入gem 'kaminari' 

rails g kaminari:config 而後設置默認每頁數量,重啓服務器。

在控制器@trains = Train.order(:number).page(params[:page]) 

在views:

# 加入分頁功能
json.meta1 do
  json.current_page @trains.current_page
  json.total_pages @trains.total_pages
  json.per_page @trains.limit_value
  json.total_trains Train.count
 
  if @trains.current_page == @trains.total_pages
    json.next_url nil # 最後一頁就沒有下一頁了
  else
    json.next_url api_v1_trains_url(page: @trains.next_page )
  end
 
  if @trains.current_page == 1
    json.previous_url nil # 第一頁就沒有上一頁
  else
    json.previous_url api_v1_trains_url( :page => @trains.prev_page )
  end
end

 

 


 

給Train加一個圖片

has_one_attached :image

上傳: 

train.logo.attach(io: File.open("/Users/chentianwei/Desktop/bose.jpg"),

 filename: "bose.jpg",

 content_type: "image/jpg") 

 


 

合併分支 :

git checkout master

git merge train_book

完成後上傳到git. https://github.com/chentianwei411

 

 


 

 

遠程倉庫若是變化了,本地就不能git push 了,須要先git fetch,但我彷佛fetch不下來?

學習git 的連接:

https://git-scm.com/ 

 


 

 

對API 進行測試 

 

以前的博客:https://www.cnblogs.com/chentianwei/p/9060522.html , http://www.cnblogs.com/chentianwei/p/9124505.html

 

 

加上gem 'rspec-rails'

10133  git checkout -b test
10134  bundle
10135  rails g rspec:install
10136  git add .
10137  git commit -m "add Rspec"
10138  git push
10139  git push --set-upstream origin test   #在遠程上創建一個分支,等待合併。

 

 

Web API 測試的重點是:

  1. 檢查回傳的 HTTP 狀態碼
  2. 檢查回傳的 JSON
  3. 檢查資料真的有被新建、修改或刪除

 

對API測試,是請求測試,即對請求的回覆結果的判斷是不是預期判斷:

使用rails g rspec:request XXX 快速生成 

 

spec/requests/auth_spec.rb 

RSpec.describe "API_V1::Auth", :type => :request do 

  HTTPVerb 路徑, params:{hash參數}

  expect(response).to have_http_status(響應數字)

  expect(response.body).to eq({hash參數}.to_json) 

end 

 

一個例子:

 

重點1:

response的網頁狀態碼能夠在controller中設置,如: 

def signup

...
render :json => { :message => "Failed", :errors => user.errors }, :status => 400
當符合條件後,會返回response的status就是400
相關文章
相關標籤/搜索