主題

Ruby on Rails 中的 OG 圖片

使用 FastOG 的訂閱制動態 API(Dynamic API)在 Ruby on Rails 中產生 OG 圖片——訂閱動態 API 後(請在儀表板中建立您的 API 金鑰)。先在免費的 OG 圖片測試工具(Free OG Image Tester)中預覽,再透過 HMAC 簽署的 URL 從您的 Rails 應用程式渲染動態的 Open Graph 圖片。

快速開始

1. 建立簽署者服務

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,兩者都會將空格輸出為 +:請先以 .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

控制器

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>

版面配置

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

輔助工具

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)表示 URL 中缺少 key 或 s。invalid_signature(401)表示正規化內容不符 — 請確認簽署訊息中的換行、兩端使用相同的 hmac_secret,以及 enc 將空格以 %20 進行百分號編碼(絕非 +)。

暴露 HMAC 密鑰

切勿在前端 JavaScript 或客戶端程式碼中暴露 FASTOG_HMAC_SECRET。

完整 API 參考文件

免費試用 — 無需註冊

在幾秒內預覽和測試動態 OG 圖片。

開啟免費 OG 圖片測試器

A BeCaSimulations site