Xây dựng một API server với Rails 5
Bài đăng này đã không được cập nhật trong 7 năm
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