Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เชี่ยวชาญการพัฒนา API ด้วย Ruby on Rails

ปัจจุบันการพึ่งพา API (Application Programming Interfaces) เป็นเรื่องปกติ ไม่ใช่แค่บริการใหญ่อย่าง Facebook และ Twitter เท่านั้น API นิยมเพราะเฟรมเวิร์กฝั่งไคลเอนต์อย่าง React, Angular และอื่นๆ เติบโตขึ้น Ruby on Rails ก็ติดตามเทรนด์นี้ Rails เวอร์ชันล่าสุดมีฟีเจอร์สร้าง API-only applications

ฟังก์ชันนี้เดิมอยู่ในเจม rails-api แต่มาตั้งแต่ Rails 5 กลายเป็นส่วนหนึ่งของ core framework พร้อมกับ ActionCable เป็นฟีเจอร์ที่คาดหวังมาก วันนี้เราจะมาเจาะลึก

บทความครอบคลุม: สร้าง API-only Rails app, โครงสร้าง routes และ controllers, ตอบกลับ JSON, เพิ่ม serializers, ตั้งค่า CORS (Cross-Origin Resource Sharing) และตัวเลือกรักษาความปลอดป้องกัน API จากการ濫用

ซอร์สโค้ดอยู่ที่ GitHub

สร้าง API-Only Application

รันคำสั่ง:

จะสร้างแอป API-only ชื่อ RailsApiDemo ต้องใช้ Rails 5 ขึ้นไป เพราะ option --api เพิ่งเพิ่มเข้ามา

เปิด Gemfile จะเห็นเล็กลงมาก: เจมอย่าง coffee-rails, turbolinks, sass-rails หายไป

config/application.rb มีบรรทัดใหม่:

หมายถึง Rails โหลด middleware ชุดน้อยกว่า: ไม่มี cookies และ sessions support นอกจากนี้ ถ้า generate scaffold จะไม่สร้าง views และ assets โฟลเดอร์ views/layouts ไม่มี application.html.erb อีกต่อไป

ความแตกต่างสำคัญ: ApplicationController สืบทอดจาก ActionController::API แทน ActionController::Base

นั่นคือแอป Rails พื้นฐานที่คุ้นเคย ต่อไปเพิ่มโมเดลเพื่อมีข้อมูลทำงาน:

ตั้งค่า associations และ validation:

models/user.rb

models/post.rb

โหลดข้อมูลตัวอย่างด้วย seeds.rb ใช้เจม faker สร้างข้อมูลสุ่ม:

Gemfile

ติดตั้ง:

แก้ seeds.rb:

db/seeds.rb

โหลดข้อมูล:

ตอบกลับด้วย JSON

สร้าง routes และ controllers สำหรับ API ปกติ nest routes ใต้ api/ และใส่ version เช่น api/v1/ หากมี breaking changes สร้าง namespace ใหม่ v2

routes ตัวอย่าง:

config/routes.rb

สร้าง routes แบบนี้:

อาจใช้ scope แทน namespace แต่จะมองหา controller ในโฟลเดอร์ controllers ไม่ใช่ controllers/api/v1 ต้องระวัง

สร้างโฟลเดอร์ api/v1 ใน controllers และเพิ่ม controllers:

controllers/api/v1/users_controller.rb

controllers/api/v1/posts_controller.rb

ต้อง namespace class ด้วย Api และ V1 modules

ตอบกลับ JSON ยังไงดี? ลอง 2 วิธี: jBuilder และ active_model_serializers เพิ่มใน Gemfile:

Gemfile

ติดตั้ง:

ใช้ jBuilder Gem

jBuilder เป็นเจมของทีม Rails ให้ DSL เรียบง่ายกำหนดโครงสร้าง JSON ใน views

แสดง posts ทั้งหมดใน action index:

controllers/api/v1/posts_controller.rb

สร้าง view ชื่อ index.json.jbuilder ใต้ api/v1:

views/api/v1/posts/index.json.jbuilder

json.array! วนลูป @posts json.id, json.title, json.body สร้าง key-value เข้าไป เข้า https://localhost:3000/api/v1/posts.json จะได้ JSON ประมาณนี้:

อยากแสดง author ด้วย? ง่าย:

ผลลัพธ์จะมี nested user object

เนื้อหาใน .jbuilder เป็น Ruby ธรรมดา ใช้ operation ต่างๆ ได้ รองรับ partials เช่นกัน:

สร้าง views/api/v1/posts/_post.json.jbuilder:

jBuilder ใช้ง่าย สะดวก แต่มีทางเลือกคือ serializers ไปดูต่อ

ใช้ Serializers

rails_model_serializers สร้างโดยทีม rails-api ให้ convention over configuration สำหรับ JSON generation กำหนด field ที่ควร serialize

Serializer แรก:

serializers/post_serializer.rb

ตอนนี้ to_json และ as_json ของ post จะใช้ config นี้

แก้ action index ให้ render json:

controllers/api/v1/posts_controller.rb

as_json จะถูกเรียกอัตโนมัติ

Serializers รองรับ relations เหมือน models และ nest ได้:

serializers/post_serializer.rb

ตอน serialize post จะมี nested user พร้อม id และ name หากสร้าง UserSerializer แยกต่างหาก exclude id:

serializers/post_serializer.rb

แต่ @post.as_json ยังคงมี user id และ name ทั้งคู่ จดจำไว้

รักษาความปลอดภัย API

ไม่ควรให้ใครทำอะไรก็ได้ผ่าน API มาเพิ่ม security check บังคับให้ส่ง token เมื่อสร้างและลบโพสต์

Token มีอายุไม่หมด สร้างตอนสมัคร เพิ่มคอลัมน์ token ใน users table:

Index ต้อง unique:

db/migrate/xyz_add_token_to_users.rb

รัน migration:

เพิ่ม before_create callback:

models/user.rb

เมธอด generate_token สร้าง token แบบสุ่มตรวจสอบ uniqueness:

models/user.rb

อาจใช้อัลกอริทึมอื่น เช่น MD5 hash ของ name + salt

สมัครสมาชิกผู้ใช้ (User Registration)

เพิ่ม API method สำหรับ register:

controllers/api/v1/users_controller.rb

ควรส่ง HTTP status codes ที่มีความหมาย ใช้ .json.jbuilder สำหรับ response (ไม่ส่ง option :json ให้ render) token ห้าม serialize ตลอดเวลา เช่นเวลา list users ต้องเก็บไว้ปลอดภัย

views/api/v1/users/create.json.jbuilder

ทดสอบการสมัครด้วยโค้ด Ruby ใช้เจม Faraday ทำ HTTP request:

api_client.rb

Parse JSON response ด้วย Oj:

api_client.rb

รันสคริปต์:

เก็บ token ที่ได้ไว้ใช้ต่อ

ยืนยันตัวตนด้วย Token

ใช้ authenticate_or_request_with_http_token จาก module ActionController::HttpAuthentication::Token::ControllerMethods:

controllers/api/v1/posts_controller.rb

เพิ่ม before_action และเมธอด authenticate:

controllers/api/v1/posts_controller.rb

ถ้าไม่มี token หรือหา user ไม่เจอ จะคืน 401 error หยุด action

สำคัญ: ต้องสื่อสารผ่าน HTTPS ไม่เช่นนั้น token อาจถูกปลอม แม้วิธีนี้ไม่ใช่ที่ดีที่สุด ในหลายกรณีควรใช้ OAuth 2 มีเจมช่วยอย่าง Doorkeeper และ oPRO

สร้างโพสต์ (Creating a Post)

เพิ่ม action create ใน PostsController:

controllers/api/v1/posts_controller.rb

ใช้ serializer แสดง JSON @user セットใน before_action แล้ว

ทดสอบ:

api_client.rb

แทนที่ token ด้วย token ที่ได้จากการสมัคร รันสคริปต์

ลบโพสต์ (Deleting a Post)

เพิ่ม action destroy:

controllers/api/v1/posts_controller.rb

อนุญาตลบเฉพาะโพสต์ของตัวเอง ถ้าลบสำเร็จ คืน 204 (no content) หรือส่ง id ที่ลบกลับไปก็ได้

ทดสอบ:

api_client.rb

เปลี่ยน post id ให้ตรงกับข้อมูลของคุณ

ตั้งค่า CORS

ต้องการให้เว็บบริการอื่นเข้าถึง API จากฝั่งไคลเอนต์ ต้องตั้งค่า CORS (Cross-Origin Resource Sharing) ให้ถูกต้อง CORS อนุญาตให้ web apps ส่ง AJAX requests ไปยัง third-party services มีเจม rack-cors ช่วยตั้งค่าได้ง่าย:

Gemfile

ติดตั้ง:

ตั้งค่าใน config/initializers/cors.rb (ไฟล์นี้มีตัวอย่างอยู่แล้ว) ตัวอย่าง config อนุญาตทุกคนเข้าถึง API ด้วยทุก method:

config/initializers/cors.rb

ป้องกันการ濫用 (Preventing Abuse)

ป้องกัน API จากการ濫用และ DoS attacks มีเจม rack-attack (จากทีม Kickstarter) ให้ blacklist/whitelist clients, limit request rate และอื่นๆ

เพิ่มใน Gemfile:

Gemfile

ติดตั้ง:

ตั้งค่าใน initializer rack_attack.rb ตัวอย่าง config จำกัดเฉพาะ localhost และ limit 5 requests/second:

config/initializers/rack_attack.rb

เพิ่ม RackAttack เป็น middleware:

config/application.rb

สรุป

จบบทความแล้ว หวังว่าคุณมั่นใจสร้าง API ด้วย Rails มากขึ้น ยังมีอีกทางเลือกอย่าง Grape framework ที่นิยมมาแล้ว สนใจลองดูได้ ถามได้เลยถ้าสงสัย ขอบคุณที่อ่าน Happy coding!