ทุกวันนี้ เป็นเรื่องปกติที่ต้องพึ่งพา API (อินเทอร์เฟซการเขียนโปรแกรมแอปพลิเคชัน) เป็นอย่างมาก ไม่เพียงแต่บริการขนาดใหญ่เช่น Facebook และ Twitter เท่านั้นที่ใช้บริการ API ยังได้รับความนิยมอย่างมากเนื่องจากมีการแพร่กระจายของเฟรมเวิร์กฝั่งไคลเอ็นต์ เช่น React, Angular และอื่นๆ อีกมากมาย Ruby on Rails กำลังติดตามแนวโน้มนี้ และเวอร์ชันล่าสุดนำเสนอคุณลักษณะใหม่ที่ช่วยให้คุณสร้างแอปพลิเคชันเฉพาะ API ได้
ในขั้นต้น ฟังก์ชันนี้ถูกรวมไว้ใน gem แยกต่างหากที่เรียกว่า rails-api แต่ตั้งแต่มีการเปิดตัว Rails 5 ตอนนี้ก็เป็นส่วนหนึ่งของแกนหลักของเฟรมเวิร์กแล้ว ฟีเจอร์นี้ร่วมกับ ActionCable นั้นน่าจะได้รับการคาดหวังมากที่สุด ดังนั้นวันนี้เราจะมาพูดถึงเรื่องนี้กัน
บทความนี้ครอบคลุมถึงวิธีสร้างแอปพลิเคชัน Rails เฉพาะ API และอธิบายวิธีจัดโครงสร้างเส้นทางและตัวควบคุม ตอบกลับด้วยรูปแบบ JSON เพิ่มซีเรียลไลเซอร์ และตั้งค่า CORS (การแชร์ทรัพยากรข้ามต้นทาง) คุณจะได้เรียนรู้เกี่ยวกับตัวเลือกบางอย่างในการรักษาความปลอดภัย API และปกป้องจากการละเมิด
แหล่งที่มาของบทความนี้มีอยู่ที่ GitHub
การสร้างแอปพลิเคชันเฉพาะ API
ในการเริ่มต้น ให้รันคำสั่งต่อไปนี้:
rails new RailsApiDemo --api
กำลังจะสร้างแอปพลิเคชัน Rails เฉพาะ API ใหม่ชื่อ RailsApiDemo
. อย่าลืมว่าการรองรับ --api
เพิ่มตัวเลือกใน Rails 5 เท่านั้น ดังนั้นตรวจสอบให้แน่ใจว่าคุณได้ติดตั้งเวอร์ชันนี้หรือเวอร์ชันที่ใหม่กว่าแล้ว
เปิด Gemfile และสังเกตว่ามันเล็กกว่าปกติมาก:อัญมณีอย่าง coffee-rails
, turbolinks
, และ sass-rails
หายไปแล้ว
ไฟล์ config/application.rb ไฟล์มีบรรทัดใหม่:
config.api_only = true
หมายความว่า Rails จะโหลดมิดเดิลแวร์ชุดที่เล็กกว่า เช่น ไม่รองรับคุกกี้และเซสชัน นอกจากนี้ หากคุณพยายามสร้างโครงนั่งร้าน มุมมองและสินทรัพย์จะไม่ถูกสร้างขึ้น ที่จริงแล้ว หากคุณตรวจสอบ มุมมอง/เลย์เอาต์ ไดเรกทอรี คุณจะสังเกตเห็นว่า application.html.erb ไฟล์ก็หายไปเช่นกัน
ความแตกต่างที่สำคัญอีกประการหนึ่งคือ ApplicationController
สืบทอดมาจาก ActionController::API
ไม่ใช่ ActionController::Base
.
เกือบแล้ว ทั้งหมดนี้เป็นแอปพลิเคชัน Rails พื้นฐานที่คุณเคยเห็นมาหลายครั้งแล้ว ตอนนี้ มาเพิ่มโมเดลสองสามแบบเพื่อให้เรามีบางอย่างที่จะใช้งาน:
rails g model User name:string rails g model Post title:string body:text user:belongs_to rails db:migrate
ไม่มีอะไรแฟนซีเกิดขึ้นที่นี่:โพสต์ที่มีชื่อและเนื้อหาเป็นของผู้ใช้
ตรวจสอบให้แน่ใจว่าได้ตั้งค่าการเชื่อมโยงที่เหมาะสมและให้การตรวจสอบการตรวจสอบอย่างง่าย:
รุ่น/user.rb
has_many :posts validates :name, presence: true
รุ่น/post.rb
belongs_to :user validates :title, presence: true validates :body, presence: true
ฉลาดหลักแหลม! ขั้นตอนต่อไปคือการโหลดระเบียนตัวอย่างสองสามรายการลงในตารางที่สร้างขึ้นใหม่
กำลังโหลดข้อมูลสาธิต
วิธีที่ง่ายที่สุดในการโหลดข้อมูลบางส่วนคือการใช้ seeds.rb ไฟล์ภายใน db ไดเรกทอรี อย่างไรก็ตาม ฉันขี้เกียจ (อย่างที่โปรแกรมเมอร์หลายคนเป็น) และไม่อยากนึกถึงเนื้อหาตัวอย่างใดๆ ดังนั้น ทำไมเราไม่ใช้ประโยชน์จากอัญมณีปลอมที่สามารถสร้างข้อมูลแบบสุ่มได้หลายประเภท:ชื่อ อีเมล คำฮิปสเตอร์ ข้อความ "lorem ipsum" และอีกมากมาย
Gemfile
group :development do gem 'faker' end
ติดตั้งอัญมณี:
bundle install
ตอนนี้ปรับแต่ง seeds.rb :
db/seeds.rb
5.times do user = User.create({name: Faker::Name.name}) user.posts.create({title: Faker::Book.title, body: Faker::Lorem.sentence}) end
สุดท้าย โหลดข้อมูลของคุณ:
rails db:seed
ตอบสนองด้วย JSON
แน่นอนว่าตอนนี้ เราต้องการเส้นทางและตัวควบคุมบางตัวเพื่อสร้าง API ของเรา เป็นเรื่องปกติที่จะซ้อนเส้นทางของ API ไว้ใต้ api/
เส้นทาง. นอกจากนี้ นักพัฒนามักจะระบุเวอร์ชันของ API ในพาธ เช่น api/v1/
. ในภายหลัง หากจำเป็นต้องทำการเปลี่ยนแปลงบางอย่าง คุณสามารถสร้างเนมสเปซใหม่ (v2
) และตัวควบคุมแยกต่างหาก
นี่คือลักษณะเส้นทางของคุณ:
config/routes.rb
namespace 'api' do namespace 'v1' do resources :posts resources :users end end
สิ่งนี้สร้างเส้นทางเช่น:
api_v1_posts GET /api/v1/posts(.:format) api/v1/posts#index POST /api/v1/posts(.:format) api/v1/posts#create api_v1_post GET /api/v1/posts/:id(.:format) api/v1/posts#show
คุณสามารถใช้ scope
เมธอดแทน namespace
แต่โดยค่าเริ่มต้นแล้ว มันจะมองหา UsersController
และ PostsController
ภายใน คอนโทรลเลอร์ ไดเร็กทอรี ไม่ใช่ใน controllers/api/v1 , ดังนั้นจงระวัง.
สร้าง api โฟลเดอร์ที่มีไดเร็กทอรีซ้อน v1 ภายใน คอนโทรลเลอร์ . เติมข้อมูลด้วยตัวควบคุมของคุณ:
controllers/api/v1/users_controller.rb
module Api module V1 class UsersController < ApplicationController end end end
controllers/api/v1/posts_controller.rb
module Api module V1 class PostsController < ApplicationController end end end
โปรดทราบว่าคุณไม่เพียงแต่ต้องซ้อนไฟล์ของคอนโทรลเลอร์ไว้ใต้ api/v1 เส้นทาง แต่ตัวคลาสเองจะต้องถูกเนมสเปซภายใน Api
และ V1
โมดูล
คำถามต่อไปคือจะตอบสนองอย่างไรกับข้อมูลที่จัดรูปแบบ JSON อย่างถูกต้อง ในบทความนี้ เราจะลองใช้วิธีแก้ปัญหาเหล่านี้:jBuilder และ active_model_serializers gems ดังนั้นก่อนที่จะไปยังส่วนถัดไป ให้วางลงใน Gemfile :
Gemfile
gem 'jbuilder', '~> 2.5' gem 'active_model_serializers', '~> 0.10.0'
จากนั้นเรียกใช้:
bundle install
การใช้ jBuilder Gem
jBuilder เป็นอัญมณียอดนิยมที่ดูแลโดยทีมงาน Rails ซึ่งมี DSL แบบง่าย (ภาษาเฉพาะโดเมน) ช่วยให้คุณกำหนดโครงสร้าง JSON ในมุมมองของคุณได้
สมมติว่าเราต้องการแสดงโพสต์ทั้งหมดเมื่อผู้ใช้กด index
การกระทำ:
controllers/api/v1/posts_controller.rb
def index @posts = Post.order('created_at DESC') end
สิ่งที่คุณต้องทำคือสร้างมุมมองที่ตั้งชื่อตามการกระทำที่เกี่ยวข้องกับ .json.jbuilder การขยาย. โปรดทราบว่ามุมมองต้องอยู่ใต้ api/v1 เส้นทางเช่นกัน:
views/api/v1/posts/index.json.jbuilder
json.array! @posts do |post| json.id post.id json.title post.title json.body post.body end
json.array!
ข้าม @posts
อาร์เรย์ json.id
, json.title
และ json.body
สร้างคีย์ด้วยชื่อที่สอดคล้องกันซึ่งตั้งค่าอาร์กิวเมนต์เป็นค่า หากคุณไปที่ https://localhost:3000/api/v1/posts.json คุณจะเห็นผลลัพธ์ที่คล้ายกับผลลัพธ์นี้:
[ {"id": 1, "title": "Title 1", "body": "Body 1"}, {"id": 2, "title": "Title 2", "body": "Body 2"} ]
จะเป็นอย่างไรถ้าเราต้องการแสดงผู้เขียนสำหรับแต่ละโพสต์ด้วย มันง่าย:
json.array! @posts do |post| json.id post.id json.title post.title json.body post.body json.user do json.id post.user.id json.name post.user.name end end
ผลลัพธ์จะเปลี่ยนเป็น:
[ {"id": 1, "title": "Title 1", "body": "Body 1", "user": {"id": 1, "name": "Username"}} ]
เนื้อหาของ .jbuilder ไฟล์เป็นรหัส Ruby ธรรมดา คุณจึงสามารถใช้การทำงานพื้นฐานทั้งหมดได้ตามปกติ
โปรดทราบว่า jBuilder รองรับบางส่วนเช่นเดียวกับมุมมอง Rails ทั่วไป ดังนั้นคุณอาจพูดว่า:
json.partial! partial: 'posts/post', collection: @posts, as: :post
แล้วสร้าง views/api/v1/posts/_post.json.jbuilder ไฟล์ที่มีเนื้อหาดังต่อไปนี้:
json.id post.id json.title post.title json.body post.body json.user do json.id post.user.id json.name post.user.name end
อย่างที่คุณเห็น jBuilder นั้นง่ายและสะดวก อย่างไรก็ตาม คุณอาจยึดติดกับซีเรียลไลเซอร์แทนได้ ดังนั้นเรามาพูดถึงมันในหัวข้อถัดไป
การใช้ซีเรียลไลเซอร์
rails_model_serializers gem สร้างขึ้นโดยทีมที่เริ่มจัดการ rails-api ตามที่ระบุไว้ในเอกสารประกอบ rails_model_serializers นำคอนฟิกูเรชันเหนือการกำหนดค่ามาสู่รุ่น JSON ของคุณ โดยพื้นฐานแล้ว คุณกำหนดว่าควรใช้ฟิลด์ใดในการทำให้เป็นอันดับ (นั่นคือ การสร้าง JSON)
นี่คือซีเรียลไลเซอร์ตัวแรกของเรา:
serializers/post_serializer.rb
class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body end
ที่นี่เราบอกว่าทุกฟิลด์เหล่านี้ควรมีอยู่ใน JSON ที่เป็นผลลัพธ์ ตอนนี้วิธีการต่างๆ เช่น to_json
และ as_json
การเรียกใช้โพสต์จะใช้การกำหนดค่านี้และส่งคืนเนื้อหาที่เหมาะสม
หากต้องการดูการทำงานจริง ให้แก้ไข index
การกระทำเช่นนี้:
controllers/api/v1/posts_controller.rb
def index @posts = Post.order('created_at DESC') render json: @posts end
as_json
จะถูกเรียกโดยอัตโนมัติเมื่อ @posts
วัตถุ
แล้วผู้ใช้ล่ะ? Serializers ช่วยให้คุณระบุความสัมพันธ์ได้เช่นเดียวกับตัวแบบ ยิ่งไปกว่านั้น Serializers สามารถซ้อนกันได้:
serializers/post_serializer.rb
class PostSerializer < ActiveModel::Serializer attributes :id, :title, :body belongs_to :user class UserSerializer < ActiveModel::Serializer attributes :id, :name end end
ตอนนี้ เมื่อคุณทำให้โพสต์เป็นอนุกรม จะมี user
. ที่ซ้อนกันโดยอัตโนมัติ คีย์พร้อมรหัสและชื่อ หากในภายหลังคุณสร้าง serializer แยกต่างหากสำหรับผู้ใช้ด้วย :id
ไม่รวมแอตทริบิวต์:
serializers/post_serializer.rb
class UserSerializer < ActiveModel::Serializer attributes :name end
แล้ว @user.as_json
จะไม่ส่งคืน ID ผู้ใช้ ยัง @post.as_json
จะส่งคืนทั้งชื่อผู้ใช้และ ID ของผู้ใช้ ดังนั้นโปรดจำไว้
การรักษาความปลอดภัยของ API
ในหลายกรณี เราไม่ต้องการให้ใครดำเนินการใดๆ โดยใช้ API เรามานำเสนอการตรวจสอบความปลอดภัยอย่างง่าย และบังคับให้ผู้ใช้ของเราส่งโทเค็นของพวกเขาเมื่อสร้างและลบโพสต์
โทเค็นจะมีอายุการใช้งานไม่จำกัดและสร้างขึ้นเมื่อลงทะเบียนของผู้ใช้ ก่อนอื่น เพิ่ม token
. ใหม่ ไปที่ user
ตาราง:
rails g migration add_token_to_users token:string:index
ดัชนีนี้ควรรับประกันความเป็นเอกลักษณ์ เนื่องจากไม่มีผู้ใช้สองคนที่มีโทเค็นเดียวกัน:
db/migrate/xyz_add_token_to_users.rb
add_index :users, :token, unique: true
ใช้การย้ายข้อมูล:
rails db:migrate
ตอนนี้เพิ่ม before_save
โทรกลับ:
รุ่น/user.rb
before_create -> {self.token = generate_token}
generate_token
วิธีส่วนตัวจะสร้างโทเค็นในวงจรที่ไม่มีที่สิ้นสุดและตรวจสอบว่าเป็นเอกลักษณ์หรือไม่ ทันทีที่พบโทเค็นที่ไม่ซ้ำกัน ให้ส่งคืน:
รุ่น/user.rb
private def generate_token loop do token = SecureRandom.hex return token unless User.exists?({token: token}) end end
คุณสามารถใช้อัลกอริธึมอื่นเพื่อสร้างโทเค็นได้ ตัวอย่างเช่น ตามแฮช MD5 ของชื่อผู้ใช้และเกลือบางส่วน
การลงทะเบียนผู้ใช้
แน่นอน เราจำเป็นต้องอนุญาตให้ผู้ใช้ลงทะเบียนด้วย เพราะไม่เช่นนั้นพวกเขาจะไม่สามารถรับโทเค็นได้ ฉันไม่ต้องการแนะนำมุมมอง HTML ใด ๆ ในแอปพลิเคชันของเรา ให้เพิ่มวิธีการ API ใหม่แทน:
controllers/api/v1/users_controller.rb
def create @user = User.new(user_params) if @user.save render status: :created else render json: @user.errors, status: :unprocessable_entity end end private def user_params params.require(:user).permit(:name) end
เป็นความคิดที่ดีที่จะส่งคืนรหัสสถานะ HTTP ที่มีความหมาย เพื่อให้นักพัฒนาเข้าใจว่าเกิดอะไรขึ้น ตอนนี้ คุณอาจให้ซีเรียลไลเซอร์ใหม่สำหรับผู้ใช้ หรือติด .json.jbuilder ไฟล์. ฉันชอบรูปแบบหลังมากกว่า (นั่นเป็นสาเหตุที่ฉันไม่ส่ง :json
ตัวเลือกในการ render
วิธี) แต่คุณสามารถเลือกวิธีใดก็ได้ อย่างไรก็ตาม โปรดทราบว่าโทเค็น ต้องไม่ จัดลำดับเสมอ เช่น เมื่อคุณส่งคืนรายชื่อผู้ใช้ทั้งหมด ข้อมูลดังกล่าวควรได้รับการดูแลอย่างปลอดภัย!
views/api/v1/users/create.json.jbuilder
json.id @user.id json.name @user.name json.token @user.token
ขั้นตอนต่อไปคือการทดสอบว่าทุกอย่างทำงานอย่างถูกต้องหรือไม่ คุณสามารถใช้ curl
คำสั่งหรือเขียนโค้ด Ruby เนื่องจากบทความนี้เกี่ยวกับ Ruby ฉันจะใช้ตัวเลือกการเข้ารหัส
การทดสอบการลงทะเบียนของผู้ใช้
ในการดำเนินการคำขอ HTTP เราจะใช้ Faraday gem ซึ่งมีอินเทอร์เฟซทั่วไปมากกว่าอะแดปเตอร์จำนวนมาก (ค่าเริ่มต้นคือ Net::HTTP
). สร้างไฟล์ Ruby แยกต่างหาก รวม Faraday และตั้งค่าไคลเอนต์:
api_client.rb
require 'faraday' client = Faraday.new(url: 'https://localhost:3000') do |config| config.adapter Faraday.default_adapter end response = client.post do |req| req.url '/api/v1/users' req.headers['Content-Type'] = 'application/json' req.body = '{ "user": {"name": "test user"} }' end
ตัวเลือกทั้งหมดเหล่านี้อธิบายได้ง่าย:เราเลือกอะแดปเตอร์เริ่มต้น ตั้งค่า URL คำขอเป็น https://localhost:300/api/v1/users เปลี่ยนประเภทเนื้อหาเป็น application/json
และจัดเตรียมเนื้อหาของคำขอของเรา
การตอบสนองของเซิร์ฟเวอร์จะมี JSON ดังนั้นในการแยกวิเคราะห์ ฉันจะใช้ Oj gem:
api_client.rb
require 'oj' # client here... puts Oj.load(response.body) puts response.status
นอกจากการตอบกลับที่แยกวิเคราะห์แล้ว ฉันยังแสดงรหัสสถานะเพื่อจุดประสงค์ในการดีบัก
ตอนนี้คุณสามารถเรียกใช้สคริปต์นี้:
ruby api_client.rb
และเก็บโทเค็นที่ได้รับไว้ที่ไหนสักแห่ง เราจะใช้ในส่วนถัดไป
ตรวจสอบสิทธิ์ด้วยโทเค็น
ในการบังคับใช้การตรวจสอบโทเค็น authenticate_or_request_with_http_token
สามารถใช้วิธีการ เป็นส่วนหนึ่งของโมดูล ActionController::HttpAuthentication::Token::ControllerMethods ดังนั้นอย่าลืมรวมไว้ด้วย:
controllers/api/v1/posts_controller.rb
class PostsController < ApplicationController include ActionController::HttpAuthentication::Token::ControllerMethods # ... end
เพิ่ม before_action
ใหม่ และวิธีการที่เกี่ยวข้อง:
controllers/api/v1/posts_controller.rb
before_action :authenticate, only: [:create, :destroy] # ... private # ... def authenticate authenticate_or_request_with_http_token do |token, options| @user = User.find_by(token: token) end end
ตอนนี้ หากไม่ได้ตั้งค่าโทเค็นหรือหากไม่พบผู้ใช้ที่มีโทเค็นดังกล่าว ข้อผิดพลาด 401 จะถูกส่งคืน เป็นการหยุดการดำเนินการจากการดำเนินการ
โปรดทราบว่าการสื่อสารระหว่างไคลเอนต์และเซิร์ฟเวอร์ต้องทำผ่าน HTTPS เพราะไม่เช่นนั้นโทเค็นอาจถูกปลอมแปลงได้ง่าย แน่นอนว่าโซลูชันที่ให้มานั้นไม่เหมาะ และในหลายกรณี ควรใช้โปรโตคอล OAuth 2 สำหรับการตรวจสอบสิทธิ์ มีอัญมณีอย่างน้อย 2 ชิ้นที่ช่วยให้กระบวนการสนับสนุนคุณลักษณะนี้ง่ายขึ้นอย่างมาก ได้แก่ Doorkeeper และ oPRO
การสร้างโพสต์
หากต้องการดูการตรวจสอบความถูกต้องของเรา ให้เพิ่ม create
การดำเนินการกับ PostsController
:
controllers/api/v1/posts_controller.rb
def create @post = @user.posts.new(post_params) if @post.save render json: @post, status: :created else render json: @post.errors, status: :unprocessable_entity end end
เราใช้ประโยชน์จาก serializer ที่นี่เพื่อแสดง JSON ที่เหมาะสม @user
ถูกกำหนดไว้แล้วใน before_action
.
ตอนนี้ทดสอบทุกอย่างโดยใช้โค้ดง่ายๆ นี้:
api_client.rb
client = Faraday.new(url: 'https://localhost:3000') do |config| config.adapter Faraday.default_adapter config.token_auth('127a74dbec6f156401b236d6cb32db0d') end response = client.post do |req| req.url '/api/v1/posts' req.headers['Content-Type'] = 'application/json' req.body = '{ "post": {"title": "Title", "body": "Text"} }' end
แทนที่อาร์กิวเมนต์ที่ส่งผ่านไปยัง token_auth
ด้วยโทเค็นที่ได้รับเมื่อลงทะเบียนและเรียกใช้สคริปต์
ruby api_client.rb
การลบโพสต์
การลบโพสต์ทำได้ในลักษณะเดียวกัน เพิ่ม destroy
การกระทำ:
controllers/api/v1/posts_controller.rb
def destroy @post = @user.posts.find_by(params[:id]) if @post @post.destroy else render json: {post: "not found"}, status: :not_found end end
เราอนุญาตให้ผู้ใช้ทำลายโพสต์ที่พวกเขาเป็นเจ้าของจริงเท่านั้น หากลบโพสต์สำเร็จ รหัสสถานะ 204 (ไม่มีเนื้อหา) จะถูกส่งคืน หรือคุณอาจตอบกลับด้วยรหัสของโพสต์ที่ถูกลบไปเนื่องจากจะยังอยู่ในหน่วยความจำ
นี่คือโค้ดสำหรับทดสอบฟีเจอร์ใหม่นี้:
api_client.rb
response = client.delete do |req| req.url '/api/v1/posts/6' req.headers['Content-Type'] = 'application/json' end
แทนที่ ID ของโพสต์ด้วยหมายเลขที่เหมาะกับคุณ
ตั้งค่า CORS
หากคุณต้องการเปิดใช้งานบริการเว็บอื่นๆ เพื่อเข้าถึง API ของคุณ (จากฝั่งไคลเอ็นต์) คุณควรตั้งค่า CORS (Cross-Origin Resource Sharing) อย่างเหมาะสม โดยทั่วไป CORS อนุญาตให้เว็บแอปพลิเคชันส่งคำขอ AJAX ไปยังบริการของบุคคลที่สาม โชคดีที่มีอัญมณีที่เรียกว่า rack-cors ที่ช่วยให้เราสามารถตั้งค่าทุกอย่างได้อย่างง่ายดาย เพิ่มลงใน Gemfile :
Gemfile
gem 'rack-cors'
ติดตั้ง:
bundle install
จากนั้นจัดเตรียมการกำหนดค่าภายใน config/initializers/cors.rb ไฟล์. อันที่จริง ไฟล์นี้ถูกสร้างขึ้นสำหรับคุณแล้วและมีตัวอย่างการใช้งาน นอกจากนี้คุณยังสามารถค้นหาเอกสารที่มีรายละเอียดสวย ๆ ได้ในหน้าของอัญมณี
ตัวอย่างเช่น การกำหนดค่าต่อไปนี้จะอนุญาตให้ทุกคนเข้าถึง API ของคุณโดยใช้วิธีการใดก็ได้:
config/initializers/cors.rb
Rails.application.config.middleware.insert_before 0, Rack::Cors do allow do origins '*' resource '/api/*', headers: :any, methods: [:get, :post, :put, :patch, :delete, :options, :head] end end
การป้องกันการใช้ในทางที่ผิด
สิ่งสุดท้ายที่ฉันจะพูดถึงในคู่มือนี้คือวิธีการปกป้อง API ของคุณจากการละเมิดและการปฏิเสธการโจมตีบริการ มีอัญมณีที่ดีที่เรียกว่า rack-attack (สร้างโดยผู้คนจาก Kickstarter) ที่ให้คุณขึ้นบัญชีดำหรือไคลเอนต์ที่อนุญาตพิเศษ ป้องกันไม่ให้เซิร์ฟเวอร์ล้นด้วยคำขอ และอื่นๆ อีกมากมาย
วางอัญมณีลงใน Gemfile :
Gemfile
gem 'rack-attack'
ติดตั้ง:
bundle install
จากนั้นให้กำหนดค่าภายใน rack_attack.rb ไฟล์เริ่มต้น เอกสารประกอบของ gem แสดงรายการตัวเลือกทั้งหมดที่มีและแนะนำกรณีการใช้งานบางกรณี นี่คือตัวอย่างการกำหนดค่าที่จำกัดไม่ให้ทุกคนยกเว้นคุณเข้าถึงบริการและจำกัดจำนวนคำขอสูงสุดที่ 5 ต่อวินาที:
config/initializers/rack_attack.rb
class Rack::Attack safelist('allow from localhost') do |req| # Requests are allowed if the return value is truthy '127.0.0.1' == req.ip || '::1' == req.ip end throttle('req/ip', :limit => 5, :period => 1.second) do |req| req.ip end end
อีกสิ่งหนึ่งที่ต้องทำคือการรวม RackAttack เป็นมิดเดิลแวร์:
config/application.rb
config.middleware.use Rack::Attack
บทสรุป
เรามาถึงจุดสิ้นสุดของบทความนี้แล้ว หวังว่าตอนนี้คุณจะรู้สึกมั่นใจมากขึ้นเกี่ยวกับการสร้าง API ด้วย Rails! โปรดทราบว่านี่ไม่ใช่ตัวเลือกเดียวที่มี—อีกวิธีหนึ่งที่ได้รับความนิยมซึ่งมีมาระยะหนึ่งคือเฟรมเวิร์กของ Grape ดังนั้นคุณอาจสนใจลองดูเช่นกัน
อย่าลังเลที่จะโพสต์คำถามของคุณหากมีบางสิ่งที่ดูเหมือนไม่ชัดเจนสำหรับคุณ ฉันขอขอบคุณที่อยู่กับฉัน และขอให้สนุกกับการเขียนโค้ด!