12/08/2018, 13:40

Grape gem tutorial: How to build a REST-Like API in Ruby

Như các lập trình viên Ruby on Rails, chúng ta thường mở rộng ứng dụng với API endpoints để support JavaScript-heavy Rich internet clients, hay các ứng dụng mobile như iPhone hay Android. Ngày nay, cũng có rất nhiều những ứng dụng có vai trò duy nhất trở thành server cho các ứng dụng Android hay ...

Như các lập trình viên Ruby on Rails, chúng ta thường mở rộng ứng dụng với API endpoints để support JavaScript-heavy Rich internet clients, hay các ứng dụng mobile như iPhone hay Android. Ngày nay, cũng có rất nhiều những ứng dụng có vai trò duy nhất trở thành server cho các ứng dụng Android hay IPhone thông qua JSON API. Trong bài viết này sẽ hướng dẫn sử dụng Grape - một REST-like API framework của Ruby để hỗ trợ xây dựng backend JSON API trong các ứng dụng Rail.

Use case

Use case trong ứng dụng demo sẽ chỉ đơn giản là đăng nhập vào hệ thống thông qua API

Getting started

Step 1: Đầu tiên, chúng ta tạo một ứng dụng rails

rails new demo_login_api

Step 2: Định nghĩa Gemfile:

...
gem 'devise'
gem 'grape'
...

Step 3: Tạo User model:

bundle install
rails g model user
rails generate devise:install
rails generate devise user

Implementing The Login API Endpoint

Trước hết, chúng ta tạo ra khung của login API(app/api/login.rb):

class Login < Grape::API
  format :json
  desc 'End-points for the Login'
  namespace :login do
    desc 'Login via email and password'
    params do
      requires :email, type: String, desc: 'email'
      requires :password, type: String, desc: 'password'
    end
    post do
    end
  end
end

Enpoint này được định nghĩa với POST method và yêu cầu 2 params truyền lên đó là email và password với format là String. Tiếp theo chúng ta tạo một class tổng hợp lại những endpoint(app/api/api.rb) và mount Login endpoint đã định nghĩa phía trên:

class API < Grape::API
  prefix 'api'
  mount Login
end

Và giờ chúng ta có thể mount các endpoints vào trong config/routes.rb như sau:

Rails.application.routes.draw do
...
  mount API => '/'
...
end

Để xử lý response trong các trường hợp request tới các endpoint bị vi phạm validation như thiếu params đã định nghĩa require hay sai format, chúng ta có thể rescue bằng cách thêm vào code xử lý ngay tại file api.rb như sau:

rescue_from Grape::Exceptions::ValidationErrors do |e|
  rack_response({
    status: e.status,
    error_msg: e.message,
  }.to_json, 400)
end

rescue_form Grape::Exceptions::ValidationErrors sẽ tự động catch mọi exception ValidationErrors nếu có khi gọi tới enpoint. Cuối cùng là việc xử lý logic cho việc authentication. Trong trường hợp invalid authentication như user không tồn tại hay password không hợp lệ, chúng ta thực hiện trả về thông báo lỗi 401 như sau (login.rb)

post do
  user = User.find_by_email params[:email]
  if user.present? && user.valid_password?(params[:password])
  else
    error_msg = 'Bad Authentication Parameters'
    error!({ 'error_msg' => error_msg }, 401)
  end
end

Trong trường hợp login thành công, response thành công trả về từ server sẽ gửi kèm theo token cho client, và client sẽ sử dụng token này cho việc xác thực các endpoint sau. Format response sẽ có dạng

{'email':<the_email_of_the_user>, 'token':<the users first valid token>}

Trong trường hợp token chưa có, chúng ta sẽ tạo một token hợp lệ rồi gửi trả về

...
if user.present? && user.valid_password?(params[:password])
  token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user)
  status 200
else
...

Đối với việc tạo token, đầu tiên chúng ta tạo model AuthenticationToken để lưu lại token như sau:

rails g model authentication_token token user:references expires_at:datetime
rake db:migrate

Thêm các quan hệ model tương ứng cho User

class User < ActiveRecord::Base
  has_many :authentication_tokens
end

và trong model AuthenticationToken

class AuthenticationToken < ActiveRecord::Base
  belongs_to :user
  validates :token, presence: true
  scope :valid,  -> { where{ (expires_at == nil) | (expires_at > Time.zone.now) } }
end

và cuối cùng format lại login response của server

...
if user.present? && user.valid_password?(params[:password])
  token = user.authentication_tokens.valid.first || AuthenticationToken.generate(user)
  status 200
  {"email": user.email, "token": token}
else
...

Như vậy là chúng ta hoàn thành việc tạo ra endpoint xử lý login API endpoint rồi. Sử dụng Grape trong ứng dụng sẽ giúp bạn nhanh chóng và thuận tiện hơn trong việc xây dựng một API. Chúc các bạn thành công.

References

Grape homepage

Demo Grape from toptal

0