หัวข้อ

OG Images ใน Ruby on Rails

สร้าง OG images ใน Ruby on Rails ด้วย Dynamic API แบบสมัครสมาชิกของ FastOG — โดยต้องมีการสมัครสมาชิก Dynamic API (สร้าง API key ของคุณในแดชบอร์ด) ดูตัวอย่างใน Free OG Image Tester จากนั้นเรนเดอร์ Open Graph images แบบไดนามิกจากแอป Rails ของคุณด้วย URL ที่ลงนามด้วย HMAC

เริ่มต้นใช้งานอย่างรวดเร็ว

1. สร้างบริการ signer

ruby
# app/services/fastog_signer.rb
class QuickogSigner
  def self.generate_signed_og_url(params, options = {})
    base_url = options[:base_url] || ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
    api_key = options[:api_key] || ENV.fetch("FASTOG_API_KEY")
    hmac_secret = options[:hmac_secret] || ENV.fetch("FASTOG_HMAC_SECRET")

    raise "Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET" unless api_key && hmac_secret

    all = params.merge(key: api_key)
    pairs = all.flat_map do |k, v|
      Array(v).sort.map { |vv| "#{enc(k)}=#{enc(vv)}" }
    end
    canonical = pairs.sort.join("&")
    signature = OpenSSL::HMAC.hexdigest("SHA256", hmac_secret, "GET/api/v1/og\n#{canonical}")
    "#{base_url}?#{URI.encode_www_form(all.merge(s: signature))}"
  end

  def self.enc(s)
    s.to_s.gsub(/[^A-Za-z0-9\-._~]/) { |c| c.bytes.map { |b| "%%%02X" % b }.join }
  end
  private_class_method :enc
end

ทำไมต้องใช้ enc แบบกำหนดเอง? เนื่องจากจะทำการเข้ารหัสเปอร์เซ็นต์ด้วยเลขฐานสิบหกตัวพิมพ์ใหญ่ โดยเหลือเฉพาะ -._~ ที่ไม่สงวนไว้ — ดังนั้นช่องว่างจะกลายเป็น %20 ไม่ใช่ + ซึ่งตรงกับ rawurlencode ของเซิร์ฟเวอร์ หากคุณเปลี่ยนไปใช้ CGI.escape หรือ URI.encode_www_form_component ทั้งคู่จะสร้าง + สำหรับช่องว่าง: ควรทำการ normalize ด้วย .gsub('+', '%20') ก่อน

2. การกำหนดค่า

ruby
# config/application.rb
config.fastog = ActiveSupport::OrderedOptions.new
config.fastog.api_key = ENV.fetch("FASTOG_API_KEY", nil)
config.fastog.hmac_secret = ENV.fetch("FASTOG_HMAC_SECRET", nil)
config.fastog.base_url = ENV.fetch("FASTOG_BASE_URL", "https://fastog.com/api/v1/og")
bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here

ตัวควบคุม (Controller)

ruby
# app/controllers/blog_controller.rb
class BlogController < ApplicationController
  def show
    @post = Post.find_by!(slug: params[:slug])

    @og_image_url = QuickogSigner.generate_signed_og_url(
      template: "blog",
      title: @post.title,
      subtitle: @post.excerpt,
      author: @post.author.name
    )
  end
end

มุมมอง

erb
<!-- app/views/blog/show.html.erb -->
<% content_for :head do %>
  <meta property="og:image" content="<%= @og_image_url %>" />
  <meta property="og:title" content="<%= @post.title %>" />
  <meta property="og:description" content="<%= @post.excerpt %>" />
<% end %>

<article>
  <h1><%= @post.title %></h1>
  <p><%= @post.excerpt %></p>
</article>

เค้าโครง (Layout)

erb
<!-- app/views/layouts/application.html.erb -->
<head>
  <%= yield :head %>
</head>

Helper

ruby
# app/helpers/fastog_helper.rb
module QuickogHelper
  def og_image_url(template:, **params)
    QuickogSigner.generate_signed_og_url({ template: template }.merge(params))
  end
end
erb
<!-- In view -->
<meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />

ตัวอย่างอีคอมเมิร์ซ

ruby
# app/controllers/products_controller.rb
class ProductsController < ApplicationController
  def show
    @product = Product.find(params[:id])

    @og_image_url = QuickogSigner.generate_signed_og_url(
      template: "ecommerce",
      title: @product.name,
      price: @product.formatted_price,
      image: @product.image_url
    )
  end
end

ตัวอย่างบทความบล็อก

ruby
# app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find_by!(username: params[:username])

    @og_image_url = QuickogSigner.generate_signed_og_url(
      template: "blog",
      title: @user.name,
      subtitle: @user.bio,
      author: @user.name
    )
  end
end

ปลายทาง API

ruby
# config/routes.rb
get "api/og", to: "api/og#generate"
ruby
# app/controllers/api/og_controller.rb
module Api
  class OgController < ApplicationController
    skip_before_action :verify_authenticity_token

    def generate
      title = params[:title]
      template = params[:template] || "default"

      unless title
        return render json: { error: "Missing title" }, status: :bad_request
      end

      og_image_url = QuickogSigner.generate_signed_og_url(
        template: template,
        title: title
      )

      render json: { ogImageUrl: og_image_url }
    end
  end
end

Turbo/Hotwire

erb
<!-- app/views/blog/_post.html.erb -->
<%= turbo_frame_tag "post_#{@post.id}" do %>
  <meta property="og:image" content="<%= og_image_url(template: 'blog', title: @post.title) %>" />
  <h2><%= link_to @post.title, blog_path(@post) %></h2>
<% end %>

ข้อผิดพลาดที่พบบ่อย

การเซ็นข้อความผิด

❌ ผิด: "GET/api/v1/og#{canonical}" — ขาดตัวคั่นบรรทัดใหม่ ✅ ถูก: "GET/api/v1/og\n#{canonical}" — เมธอด + พาธ, ขึ้นบรรทัดใหม่, แล้วตามด้วยคิวรีตามรูปแบบบัญญัติ

การละเว้น key ออกจากลายเซ็น

❌ ผิด: เซ็นเฉพาะพารามิเตอร์เนื้อหาของคุณ แล้วผนวก key เข้ากับ URL ทีหลัง ✅ ถูก: key ถูกผสานเข้ากับพารามิเตอร์ก่อนที่จะสร้างคิวรีตามรูปแบบบัญญัติ และปรากฏใน URL สุดท้าย

401 missing_signature / invalid_signature

missing_signature (401) หมายความว่า key หรือ s หายไปจาก URL invalid_signature (401) หมายความว่าคิวรีตามรูปแบบบัญญัติไม่ตรงกัน — ตรวจสอบการขึ้นบรรทัดใหม่ในข้อความที่เซ็น, hmac_secret ที่เหมือนกันทั้งสองฝั่ง, และว่า enc เข้ารหัสเปอร์เซ็นต์ช่องว่างเป็น %20 (ไม่ใช่ +)

การเปิดเผย HMAC Secret

ห้ามเปิดเผย FASTOG_HMAC_SECRET ใน JavaScript ฝั่ง frontend หรือโค้ดฝั่งไคลเอนต์โดยเด็ดขาด

เอกสารอ้างอิง API ฉบับเต็ม

ลองใช้ฟรี — ไม่ต้องสมัครสมาชิก

ดูตัวอย่างและทดสอบภาพ OG แบบไดนามิกได้ในไม่กี่วินาที

เปิดตัวทดสอบภาพ OG ฟรี

A BeCaSimulations site