Leon's Blogging

Coding blogging for hackers.

Rails Routes設定

| Comments

要如何設計網站的 router 是非常重要的一件事。
在 rails 當中也有許多方法可以做設定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#直接設定
get '/patients/:id', to: 'patients#show'

#REST
#設定 scope 有點像資料夾的概念,可用來區分後台可控制範圍
namespace do
  #也可用單數 resource (會少了 index,然後不用帶id)
  resources :articles, only: [:index, :show] do
    member do #Acts on a single resource
      post :follow
    end
    collection do #Acts on a collection of resources
      get :follow
    end
  end
end

scope

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
get 'foo/meetings/:id', :to => 'events#show'
post 'foo/meetings', :to => 'events#create'

可以改寫成

scope :controller => "events", :path => "/foo", :as => "bar" do
  get 'meetings/:id', to: :show, as: "meeting"
  post 'meetings', to: :create , as: "meetings"
end

# bar_meeting GET   /foo/meetings/:id(.:format) events#show
# bar_meetings POST /foo/meetings(.:format)     events#create

scope :path => '/api/v1/', :module => "api_v1", :as => 'v1' do
  resources :projects
end

# GET  /api/v1/projects(.:format) api_v1/projects#index
# POST /api/v1/projects(.:format) api_v1/projects#create

resources :users do
  member do
    scope 'graphs' do
     get :dashboard, to: "users/graphs#dashboard"
    end
  end
end

# /users/:id/graphs/dashboard
  • :as 增加 v1_path(相對路徑) 和 v1_url(絕對路徑)
  • :path網址後面的 path 改成 /api/v1/projects
  • :controller 指定 controller 是哪個
  • :module 指定 controller 對應到 ApiV1::ProjectsController
  • params:id 改成指定的變數 ex params: :api_id
1
2
3
4
5
6
7
resources :api, path:'/test', controller: 'hello/api', param: :api_id, only: %i(index show create update)

api_index GET   /test(.:format)         hello/api#index
          POST  /test(.:format)         hello/api#create
      api GET   /test/:api_id(.:format) hello/api#show
          PATCH /test/:api_id(.:format) hello/api#update
          PUT   /test/:api_id(.:format) hello/api#update

redirect

1
2
3
4
5
6
7
8
# 靜態
get "/welcome" to: redirect("/hello")

# 動態
get '/stories/:name', to: redirect('/articles/%{name}')

# 導外
 get '/welcome', to: redirect('https://google.com')

redirect 將網址導向到另一個網址

設定沒有指定的 path 都導向同一個頁面

1
get "*path", to: "welcome#welcome", :defaults => { :format => :json }

defaults 設定此 routes 輸出都是 json 格式

特殊條件限定

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#過濾id
get 'photos/:id', to: 'photos#show', id: /[A-Z]\d{5}/

#黑名單
class BlacklistConstraint
  def initialize
    @ips = Blacklist.retrieve_ips
  end

  def matches?(request)
    @ips.include?(request.remote_ip)
  end
end

Rails.application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: BlacklistConstraint.new
end

or

Rails.application.routes.draw do
  get '*path', to: 'blacklist#index',
    constraints: lambda { |request| Blacklist.retrieve_ips.include?(request.remote_ip) }
end

子網域

1
resources :posts, constraints: { subdomain: 'api' }
1
2
3
constraints subdomain: 'api' do
  resources :posts  #http://api.mgleon08.com/posts
end
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
namespace :api do
  constraints subdomain: 'api' do
    resources :posts
  end
end

#產生兩個 api
#http://api.mgleon08.com/api/posts

constraints subdomain: 'api' do
  namespace :api, path: '/' do #加上 path 就可以消去後面的
     resources :zombies
   end
end
#上下一樣
namespace :api, path: '/', constraints: { subdomain: 'api' } do
  resources :zombies
end

#http://api.mgleon08.com/posts

限制

1
2
# 所有沒有設定的 GET routes 都會導到 application#index
match "*path", to: "application#index", format: false, via: :get

DRY

none

1
2
# 不需要任何 resources 產生的 restful,可能是要做 member or collection
resources :users, only: :none

concern

1
2
3
4
5
6
7
8
9
10
concern :sociable do |options|
  resources :comments, options
  resources :categories, options
  resources :tags, options
end
resources :messages, concerns: :sociable
resources :posts,    concerns: :sociable
resources :items do
  concerns :sociable, only: :create
end

or

1
2
3
4
5
6
concern :sociable, Sociable
resources :messages, concerns: :sociable
resources :posts, concerns: :sociable
resources :items do
  concerns :sociable, only: :create
end
1
2
3
4
5
6
7
8
#app/concerns/sociable.rb
class Sociable
  def self.call(mapper, options)
    mapper.resources :comments, options
    mapper.resources :categories, options
    mapper.resources :tags, options
  end
end

合併

1
2
resources :zombies, only: :index
resources :humans, only: :inde
1
2
3
4
5
with_options only: :index do |list_only|
   list_only.resources :zombies
   list_only.resources :humans
   list_only.resources :medical_kits
end

將 module 名稱改為大寫

1
2
3
4
5
#app/controllers/api/posts_controller.rb
module Api #通常是camelcase
   class PostController < ApplicationController
   end
end
1
2
3
4
#config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'API'
end

CURL

可以用 command line 來測試 get

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
curl -i localhost:3000/posts
#-i 詳細資訊

curl -IH "Accept: application/json" localhost:3000/posts
curl -IH "Accept: application/xml" localhost:3000/posts
#H HEAD

curl -i -X POST -d 'episode[title]=ZombieApocalypseNow' http://localhost:3000/posts

#-X the -X option specifies the method
#-d use -d to send data on the request

curl -Iu 'carlos:secret' http://localhost:3000/posts
#上下一樣
curl -I http://carlos:secret@localhost:3000/episodes
#send Basic Auth credentials with the -u option

curl -IH "Accept: application/json" -u 'carlos:fakesecret' http://localhost:3000/posts
#client asks for JSON

curl -IH "Authorization: Token token=16d7d6089b8fe0c5e19bfe10bb156832" http://localhost:3000/posts
#Set token on Authorization header

路由參數

1
get '/clients/:status' => 'clients#index', foo: 'bar'

當使用者打開 /clients/active 這一頁,params[:status] 便會被設成 “active",params[:foo] 也會被設成 "bar",就像是我們原本透過 Query String 傳進去那樣。同樣的,params[:action] 也會被設成 index。

default_url_options

每次都會在指定的參數進來

1
2
3
4
5
class ApplicationController < ActionController::Base
  def default_url_options
    { locale: I18n.locale }
  end
end

shallow

編輯、更新 以及 刪除 功能沒有必要一定要跟在 User 後面,因為主要就是要檢視文章

1
2
3
4
resources :users do
  resources :posts, only: [:index, :new, :create]
end
resources :posts, only: [:show, :edit, :update, :destroy]

上下相等

1
2
3
resources :users do
  resources :posts, shallow: true
end

後台網址不要太好猜

/test/products 對應資料夾 admin/products#index

1
2
3
namespace :admin, path: "test" do
  resources :products
end

單數 resource

當使用者能檢視、更新、刪除自己的 profile,就能夠用單數的 resource

需要檢視每個人的話就用複數,像是管理員

1
2
3
Rails.application.routes.draw do
  resource :profile
end
1
2
3
4
5
6
7
8
  Prefix Verb   URI Pattern             Controller#Action
 profile POST   /profile(.:format)      profiles#create
 new_profile GET    /profile/new(.:format)  profiles#new
edit_profile GET    /profile/edit(.:format) profiles#edit
         GET    /profile(.:format)      profiles#show
         PATCH  /profile(.:format)      profiles#update
         PUT    /profile(.:format)      profiles#update
         DELETE /profile(.:format)      profiles#destroys

On

1
2
3
4
5
6
resources :orders do
  member do
    post :confirm
    delete :cancel
  end
end

可改寫為

1
2
3
4
resources :orders do
  post :confirm, on: :member
  delete :cancel, on: :member
end

參考文件:

Comments