Cách xây dụng một API đơn giản trong ứng dụng Rails của bạn <Part 1>
Bài đăng này đã không được cập nhật trong 3 năm
Introduction
Một trong những lý do khiến cho người tiêu dùng phổ thông (không có hiểu biết về lập trình) tại Việt Nam không thực sự hiểu rõ về API
là bởi tên gọi tiếng Việt khá tối nghĩa: giao diện lập trình ứng dụng
. Lập trình ứng dụng
thì đúng nghĩa, nhưng giao diện
thì không hẳn là chính xác.
Nguyên gốc API
viết đầy đủ trong tiếng Anh là Application Programming Interface
, trong đó chữ Interface
đang bị dịch thành giao diện
. Thực ra, trong các bối cảnh khác thì cách dịch này là chính xác, ví dụ như GUI (Graphical User Interface)
dịch thành giao diện đồ họa người dùng
, còn CLI (Command Line Interface)
dịch thành giao diện dòng lệnh
. Từ giao diện
ở đây được hiểu là bề mặt
để con người tương tác với máy, như khi chúng ta dùng cửa sổ để tương tác với Windows hoặc dùng các câu lệnh để tương tác với DOS.
Nhưng nếu bạn mang cách hiểu giao diện
như trong các cụm từ "giao diện cửa sổ", "giao diện cảm ứng", "giao diện iOS" để áp dụng vào từ giao diện
trong API
thì bạn đã hiểu sai. API
là một giao diện
giữa phần mềm với phần mềm.
API
là cách để các phần mềm (hệ điều hành, ứng dụng, các module trong hệ thống doanh nghiệp...) giao tiếp với nhau và tận dụng năng lực của nhau.
Ví dụ một vài API
lớn mà tôi và các bạn đã từng biết: Google API, Facebook API, Twitter API, PayPal API...
Bài viết hôm nay mình sẽ giới thiệu các bước cơ bản để xây dựng một API
trong Rails
là như thế nào, mời các bạn tìm hiểu các bưới bên dưới.
API Resource
Đầu tiên là chúng ta sẽ tách riêng phần API
ra khỏi phần còn lại của ứng dụng. Để làm được điều đó chúng ta sẽ tạo một điều khiển (Controller)
mới dưới một không gian tên riêng (namespace)
. Ở đây tôi sẽ đặt tên API
sẽ là như sau (Các bạn có thể đặt khác cũng được tuy bạn)
: app/controllers/api/v1/
, API
của tôi sẽ nằm trong app/controller
.
class Api::V1::BaseController < ApplicationController
end
Ở đây API
của tôi nó được kế thừa từ ApplicationController
hoặc bạn cũng có thể viết API
kết thừa từ Controller
nào đó, ví dụ: ActionController::Base
. Chúng ta có thể ghi đè (override)
các hàm bên trong nó.
Chúng ta cần phải vô hiệu hóa CSRF token
và tắt cookie
(no set-cookies header in response).
class Api::V1::BaseController < ApplicationController
protect_from_forgery with: :null_session
before_action :destroy_session
def destroy_session
request.session_options[:skip] = true
end
end
Bây giờ chúng ta sẽ thêm router
vào API
bằng cách sử dụng giao thức RESTFul
. Nhân tiện nói về giao thức RESTFul
các bạn có thể tham khảo tài liệu mô tả về REST ở đây nhé, tài liệu viết rất đầy đủ và chi tiết (good).
Trả về dữ liệu khi gọi API
chúng ta có thể trả về dữ liệu dưới dạng JSON
hoặc cũng có thể gọi dữ liệu thông qua API REST
để trả về dữ liệu dưới dạng siêu dữ liệu như (Hyperdata or Hypermedia)
.
#api
namespace :api do
namespace :v1 do
resources :users, only: [:index, :create, :show, :update, :destroy]
resources :microposts, only: [:index, :create, :show, :update, :destroy]
end
end
Khi trả về một bản ghi (record)
chúng ta sử dụng phương thức GET
trong giao thức REST
. Dữ liệu trả về trong ActiveModelSerializers
dưới dạng JSON serialization
. Bây giờ tôi sẽ tạo 1 Controller
trong API
để lấy ra dữ liệu 1 bản ghi với phương thức GET
trả về JSON
như đoạn code bên dưới:
class Api::V1::UsersController < Api::V1::BaseController
def show
user = User.find(params[:id])
render(json: Api::V1::UserSerializer.new(user).to_json)
end
end
Một điều mà tôi muốn xây dựng các API
trong Rails
là bộ điều khiển là phải gắn gọn và dễ hiểu.
Tôi sẽ thêm 1 file user serializer
để khởi tạo đối tượng người dùng: app/serializers/api/v1/user_serializer.rb
# app/serializers/api/v1/user_serializer.rb
class Api::V1::UserSerializer < Api::V1::BaseSerializer
attributes :id, :email, :name, :activated, :admin, :created_at, :updated_at
has_many :microposts
has_many :following
has_many :followers
def created_at
object.created_at.in_time_zone.iso8601 if object.created_at
end
def updated_at
object.updated_at.in_time_zone.iso8601 if object.created_at
end
end
Nếu bây giờ tôi gửi 1 request
là api/v1/users/1
thì kết quả trả về dưới dạng json
như sau:
{
"user": {
"id": 1,
"email": "vo.van.do@framgia.com",
"name": "Vo Van Do",
"activated": true,
"admin": "admin",
"created_at": "2016-09-22T13:21:28Z",
"updated_at": "2016-09-22T13:21:28Z",
"micropost_ids": [
10,
11
],
"following_ids": [
1,
2
],
"follower_ids": []
}
}
Trường hợp request
trả về nil
, chúng ta sẽ điều hướng về lỗi 404
đã viết ở trên và kết quả trả về 404: Not found
. Chúng ta sẽ phải xây dựng hàm trả về lỗi dữ liệu không tồn tại hay request
trả về nil
. Các bạn có thể xem đoạn code bên dưới:
rescue_from ActiveRecord::RecordNotFound, with: :not_found
def not_found
return api_error(status: 404, errors: 'Not found')
end
Như vậy chúng ta đã xử lý xong trường hợp ngoại lệ mà request
trả về khi không có dữ liệu.
Hàm Index
Bài toán đặt ra là làm sao để có thể lấy hết dữ liệu của một đối tượng nào đó. Thì trong hàm này chúng ta sẽ làm điều đó.
Trong hàm Index
này chúng ta sẽ lấy ra tất cả bản ghi được lưu trữ của 1 đối tượng nào đó, cụ thể ở đây mình sẽ lấy ra tất cả dữ liệu của đối tượng User
. Ở đây chúng ta vẫn sử dụng phương thức GET
trong giao thức REST
để trả về dữ liệu.
class Api::V1::UsersController < Api::V1::BaseController
def index
users = User.all
render(
json: ActiveModel::ArraySerializer.new(
users,
each_serializer: Api::V1::UserSerializer,
root: 'users',
)
)
end
end
Khá dễ dàng phải không?
Dữ liệu trả về khi gọi hàm index
sẽ như sau:
{
"user": {
"id": 1,
"email": "vo.van.do@framgia.com",
"name": "Vo Van Do",
"activated": true,
"admin": "admin",
"created_at": "2016-09-22T13:21:28Z",
"updated_at": "2016-09-22T13:21:28Z",
"micropost_ids": [
10,
11
],
"following_ids": [
1,
2
],
"follower_ids": []
},
"user": {
"id": 2,
"email": "test@example.com",
"name": "Test",
"activated": true,
"admin": "guest",
"created_at": "2016-09-22T13:21:28Z",
"updated_at": "2016-09-22T13:21:28Z",
"micropost_ids": [
7,
8
],
"following_ids": [
4,
5
],
"follower_ids": [
5
]
},
.......................
}
Đơn giản phải không các bạn? Ngoài ra bạn có thể sử dụng thêm Gem
kollegorna để thêm các điều kiện params
và trong hàm index
. Ở ví dụ code bên dưới tôi sẽ include
thêm gem kollegorna
vào hàm index
, code sẽ như bên dưới:
class Api::V1::UsersController < Api::V1::BaseController
include ActiveHashRelation
def index
users = User.all
users = apply_filters(users, params)
render(
json: ActiveModel::ArraySerializer.new(
users,
each_serializer: Api::V1::UserSerializer,
root: 'users',
)
)
end
end
So với code phần trên thì bạn đã thấy có thêm 2 sự thay đổi, đó là chúng ta thêm dòng include ActiveHashRelation
và thêm dòng users = apply_filters(users, params)
.
Đến đây là kết thúc phần 1 của bài viết, các bạn nhớ theo dõi bài viết số 2 nhé, có rất nhiều điều thú vị ở bài số 2. Ok, chúc các bạn buổi tối vui vẻ.
Kết Luận
Bài viết này đã giới thiệu đến các bạn các bước đầu tiên cần chuẩn bị để xây dựng API
trong Rails
.
API
ngày càng được sử dụng rất rộng dãi trong lập trình ứng dụng nói riêng hay trong lập trình nói chung. Nếu thiếu API
thì tôi tin chắc rằng thế giới này sẽ không còn chuyển động theo đúng quỹ đạo của nó nữa.
Tài liệu tham khảo
All rights reserved