Tạo API trong Rails 5
Bài đăng này đã không được cập nhật trong 8 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
ApplicationControllerkế thừaActionController::APIthay vìActionController::Basetrong 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_idtrong bảngitems - Và từ đó chúng ta phải thiết lập liên kết
belongs_totrong 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_todocallback 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ìActiveRecordsẽ 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