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