Cách xây dụng một API đơn giản trong ứng dụng Rails của bạn <Part 1>
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 ...
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.
Đầ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.
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ẻ.
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.
- Using Rails for API-only Applications
- Ruby on Rails API documentation tool
- Gem Kollegorna