Tạo API trong Rails 5
Bài đăng này đã không được cập nhật trong 7 năm
Như chúng ta đã biết thì Rails là Framework dùng để build 1 ứng dụng web, bên cạnh đó Rails còn hỗ trợ để xây dụng ứng dụng API. Nên trong loạt bài này mình sẽ giới thiệu đến các bạn việc xây dựng 1 ứng dụng API bằng rails như thế nào, về cơ bản sẽ giúp các bạn hình dung ra các tạo ứng dung API đơn giản với Rails.
Ứng dụng chúng ta sẽ xây dựng là Todo-list API.
API Endpoints
Chúng ta sẽ xây dựng API dựa trên chuẩn RESTful API. và RESTful endpoints của ứng dụng sẽ là như sau :
Endpoint | Chức năng |
---|---|
POST /signup | Signup |
POST /auth/login | Login |
GET /auth/logout | Logout |
GET /todos | List all todos |
POST /todos | Create a new todo |
GET /todos/:id | Get a todo |
PUT /todos/:id | Update a todo |
DELETE /todos/:id | Delete a todo and its items |
GET /todos/:id/items | Get a todo item |
PUT /todos/:id/items | Update a todo item |
DELETE /todos/:id/items | Delete a todo item |
Thiết lập Project
Chúng ta sẽ tạo 1 Project Todos api như sau:
$ rails new todos-api --api
- Chú ý 1 tý thì việc sử dụng "--api" ở đây chúng ta muốn một ứng dụng Rails API, tức là tạo
ApplicationController
kế thừaActionController::API
thay vìActionController::Base
trong các ứng dụng Rails chúng ta vẫn thường hay làm.
Tạo Model
Chúng ta sẽ tạo Model Todo
$ rails g model Todo title:string created_by:string
Bằng việc sử dụng generate command chúng ta đã tạo 1 model Todo
như sau:
class CreateTodos < ActiveRecord::Migration[5.0]
def change
create_table :todos do |t|
t.string :title
t.string :created_by
t.timestamps
end
end
end
Tiếp theo mà model Item
$ rails g model Item name:string done:boolean todo:references
Bằng việc sử dụng todo:references
chúng ta đã tạo ra 1 liên kết giữa Item
với Todo
mode, và nó sẽ như thế này:
- Nó sẽ add 1 khóa ngoài
todo_id
trong bảngitems
- Và từ đó chúng ta phải thiết lập liên kết
belongs_to
trong modelItem
class CreateItems < ActiveRecord::Migration[5.0]
def change
create_table :items do |t|
t.string :name
t.boolean :done
t.references :todo, foreign_key: true
t.timestamps
end
end
end
Giờ thì migrations để nó tạo bảng trong DB thôi:
$ rails db:migrate
Ở 2 model của chúng ta sẽ như thế này:
# app/models/todo.rb
class Todo < ApplicationRecord
# model association
has_many :items, dependent: :destroy
# validations
validates_presence_of :title, :created_by
end
# app/models/item.rb
class Item < ApplicationRecord
# model association
belongs_to :todo
# validation
validates_presence_of :name
end
Tiếp theo cái không thể thiếu đó mà Controller, chúng ta sẽ generate 2 controller Todos
và Items
$ rails g controller Todos
$ rails g controller Items
Giờ thì định nghĩa routes
# config/routes.rb
Rails.application.routes.draw do
resources :todos do
resources :items
end
end
Trong routes
chúng ta đang định nghĩa todo resources
có một nested là items resources
. điều này thể hiện cho quan hệ 1-many
để có thể thấy được cụ thể các routes, ban có thể thực hiện :
$ rails routes
Tạo Controller
Giờ thì hãy định nghĩa Controller:
# app/controllers/todos_controller.rb
class TodosController < ApplicationController
before_action :set_todo, only: [:show, :update, :destroy]
# GET /todos
def index
@todos = Todo.all
json_response(@todos)
end
# POST /todos
def create
@todo = Todo.create!(todo_params)
json_response(@todo, :created)
end
# GET /todos/:id
def show
json_response(@todo)
end
# PUT /todos/:id
def update
@todo.update(todo_params)
head :no_content
end
# DELETE /todos/:id
def destroy
@todo.destroy
head :no_content
end
private
def todo_params
# whitelist params
params.permit(:title, :created_by)
end
def set_todo
@todo = Todo.find(params[:id])
end
end
json_response
đây là hepler trả về JSON và HTTP status, cái này sẽ được định nghĩa trong concerns
folder
# app/controllers/concerns/response.rb
module Response
def json_response(object, status = :ok)
render json: object, status: status
end
end
set_todo
callback sẽ thực hiện tìm kiếm 1 todo bằng id, nhưng nếu trong trường hợp không có record nào tồn tại thìActiveRecord
sẽ ném ra 1 exceptionActiveRecord::RecordNotFound
, chúng ta sẽ xử lý exception này và trả về thông báo 404.
# app/controllers/concerns/exception_handler.rb
module ExceptionHandler
extend ActiveSupport::Concern
included do
rescue_from ActiveRecord::RecordNotFound do |e|
json_response({ message: e.message }, :not_found)
end
rescue_from ActiveRecord::RecordInvalid do |e|
json_response({ message: e.message }, :unprocessable_entity)
end
end
end
create
method trong TodosController
, ở đây chúng ta sử dụng create!
thay vì sử dụng create
, cách này sẽ ném ra exception ActiveRecord::RecordInvalid
vì thế chúng ta sẽ xử lý exception này trong ExceptionHandler
module.
Tuy nhiên nếu chỉ như vậy thì controller của chúng ta sẽ ko thể biết được về sự tồn tại của helpers này, vậy nên chúng ta sẽ including nó trong application controller
# app/controllers/application_controller.rb
class ApplicationController < ActionController::API
include Response
include ExceptionHandler
end
Vâng , đến đây thì có thể check xem nó chạy như thế nào được rồi .
$ rails s
và thực hiện tạo request với Postman
hoặc có thể là httpie
# GET /todos
$ http :3000/todos
# POST /todos
$ http POST :3000/todos title=123123 created_by=1
# PUT /todos/:id
$ http PUT :3000/todos/1 title=qweqweqwe
# DELETE /todos/:id
$ http DELETE :3000/todos/1
Giờ thì chúng ta sẽ viết Controller co Item của Todo.
# app/controllers/items_controller.rb
class ItemsController < ApplicationController
before_action :set_todo
before_action :set_todo_item, only: [:show, :update, :destroy]
# GET /todos/:todo_id/items
def index
json_response(@todo.items)
end
# GET /todos/:todo_id/items/:id
def show
json_response(@item)
end
# POST /todos/:todo_id/items
def create
@todo.items.create!(item_params)
json_response(@todo, :created)
end
# PUT /todos/:todo_id/items/:id
def update
@item.update(item_params)
head :no_content
end
# DELETE /todos/:todo_id/items/:id
def destroy
@item.destroy
head :no_content
end
private
def item_params
params.permit(:name, :done)
end
def set_todo
@todo = Todo.find(params[:todo_id])
end
def set_todo_item
@item = @todo.items.find_by!(id: params[:id]) if @todo
end
end
và thực hiện kiếm tra API từ các request:
# GET /todos/:todo_id/items
$ http :3000/todos/2/items
# POST /todos/:todo_id/items
$ http POST :3000/todos/2/items name='zxczxc' done=false
# PUT /todos/:todo_id/items/:id
$ http PUT :3000/todos/2/items/1 done=true
# DELETE /todos/:todo_id/items/1
$ http DELETE :3000/todos/2/items/1
Trong phần sau chúng ta sẽ thực hiện Authentication với JWT (JSON WEB TOKEN).
All rights reserved