0

Xây dựng một API server với Rails 5

Rails là một framework hỗ trợ rất tốt cho việc xây dựng một server-rendered web applications. Nó có hỗ trợ cookie, session, và các chức năng trình duyệt cụ thể khác. Nó cũng tuyệt vời cho việc xây dựng các API, nhưng tại sao lại có cả một loạt chức năng mà chúng ta sẽ không sử dụng nếu điều chúng ta muốn là chỉ cần xây dựng một API?

Đó là lý do chúng ta sử dụng Rails trong chế độ api. Nó cho chúng ta sức mạnh của Rails nhưng chỉ với chức năng mà chúng ta thực sự cần cho API. Trong bài này, chúng ta sẽ tìm hiểu làm thế nào để cấu hình một Rails API đơn giản cùng với Rails 5 và ruby-gems

Sử dụng Rails API mode

Ở các phiên bản trước Rails 5 thì nếu muốn sử dụng API mode thì chúng ta phải sử dụng thêm gem rails-api. Khi Rails 5 ra mắt thì nó đã được tính hợp sẵn.

Tạo ứng dụng

rails new my_api --api

Chắc chắn là bạn đang dùng ruby 2.2 trở lên

Trong config / application.rb thêm dòng sau ở đầu của định nghĩa lớp Ứng dụng:

config.api_only = true

Sự khác biệt của Rails API Đối với phần lớn, chế độ API đã loại bỏ các chức năng mà bạn không thực sự cần khi xây dựng một API. Điều này bao gồm các vấn đề như session, cookies, assets, và thực sự bất cứ điều gì liên quan đến việc Rails làm việc với một trình duyệt. Nó cũng sẽ thay đổi generators để nó không tạo ra các view, helpers, and assets. Cụ thể, khi chạy rake middleware trên một ứng dụng api và một ứng dụng thông thường, ứng dụng thông thường bao gồm các phần mềm trung gian sau đây mà API thì không:

use #<ActiveSupport::Cache::Strategy::LocalCache::Middleware:0x007fa7511b02b0>
use Rack::MethodOverride
use WebConsole::Middleware
use ActionDispatch::Cookies
use ActionDispatch::Session::CookieStore
use ActionDispatch::Flash

Sự khác biệt cũng có thể được nhìn thấy khi bạn so sánh các ApplicationController trên một ứng dụng web so với một ứng dụng API. Phiên bản web extend từ ActionController :: Base, trong khi phiên bản API extend ActionController :: API, bao gồm một tập hợp các chức năng nhỏ hơn nhiều. Web ApplicationController:

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
end

API ApplicationController:

class ApplicationController < ActionController::API
end

Responding với JSON

Serializing Gem active_model_serializers sẽ được tự động thêm vào Gemfile nếu bạn create một Rails API application còn không thì các bạn có thể thêm bằng tay vào Gemfile

gem 'active_model_serializers'

ActiveModel :: Serializer cho phép bạn xác định thuộc tính và mối quan hệ nào bạn muốn đưa vào response JSON của bạn. Nó cũng hoạt động như mộtpresenter, nơi bạn có thể xác định các phương pháp tùy chỉnh để hiển thị thông tin bổ sung hoặc ghi đè lên cách nó được hiển thị trong JSON của bạn.

class UserSerializer < ActiveModel::Serializer
  attributes :id, :email, :name, :nickname, :birthday, :sex, :introduction

  has_one :occupation
  
  def name 
    names = object.name.split(" ") 
    "#{names[0].first}. #{names[1][7]}" 
  end 

Ở đây chúng ta chỉ muốn trả về những thuộc tính đã chỉ định attributes, name được trả về từ method name, kèm theo một occupation được lấy về từ quan hệ Bạn sẽ nhận thấy rằng mặc định nó không định dạng json: api. Để thay đổi nó thành định dạng này, chúng ta chỉ cần khai báo một initializer để báo cho ActiveModel :: Serializer cách tuần tự hóa dữ liệu JSON.

# config/initializers/active_model_serializer.rb
ActiveModel::Serializer.config.adapter = ActiveModel::Serializer::Adapter::JsonApi

Versioning

Trước khi public API, bạn nên xem xét thực hiện một số hình thức phiên bản chẳng hạn như v1 và v2, để bạn có thể duy trì khả năng tương thích ngược cho các khách hàng hiện tại bất cứ khi nào bạn giới thiệu các thay đổi API, đơn giản chỉ bằng cách tăng phiên bản. Hướng dẫn này sẽ chỉ cho bạn cách thiết lập phiên bản với định dạng URL sau:

http://api.mysite.com/v1/users/

Chúng ta có thể sử dụng một cấu trúc thư mục như thế này bằng cách xác định tất cả controllers v1 của chúng ta trong không gian tên Api :: V1:

app/controllers/
.
|-- api
|   `-- v1
|       |-- api_controller.rb
|       `-- users_controller.rb
|-- application_controller.rb

Controllers sẽ trông như thế này:

# app/controllers/api/v1/api_controller.rb

module Api::V1
  class ApiController < ApplicationController
    # Generic API stuff here
  end
end
# app/controllers/api/v1/users_controller.rb

module Api::V1
  class UsersController < ApiController

    # GET /v1/users
    def index
      render json: User.all
    end

  end
end

Và bây giờ cần phải config lại route.rb

constraints subdomain: 'api' do
  scope module: 'api' do
    namespace :v1 do
      resources :users
    end
  end
end

Rate Limiting / Throttling

Rack::Attack Đây là 1 gem cho phép chúng ta:

  • whitelist: Cho phép xử lý thông thường nếu điều kiện nhất định là đúng
  • blacklist: Gửi tin nhắn bị từ chối ngay lập tức cho các yêu cầu nhất định
  • throttle: Kiểm tra xem người dùng có nằm trong mức sử dụng cho phép của họ không
  • track: Theo dõi yêu cầu này để có thể đăng nhập một số thông tin nhất định về yêu cầu của chúng ta

Sử dụng

# Gemfile
gem 'rack-attack'
# config/application.rb
config.middleware.use Rack::Attack
module Rack
  class Attack
    # `Rack::Attack` is configured to use the `Rails.cache` value by default,
    # but you can override that by setting the `Rack::Attack.cache.store` value
    Rack::Attack.cache.store = ActiveSupport::Cache::MemoryStore.new

    # Allow all local traffic
    safelist('allow from localhost') do |req|
      # Requests are allowed if the return value is truthy
      req.ip == '127.0.0.1' || req.ip == '::1'
    end

    # Allow an IP address to make 5 requests every 5 seconds
    throttle('req/ip', limit: 5, period: 5, &:ip)
  end
end

All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí