หัวข้อ

OG Images ใน Python

คู่มือนี้แสดงวิธีสร้าง OG images ใน Python ด้วย FastOG — ไม่ว่าจะเป็น Django, FastAPI หรือ Flask — โดยการเซ็น HMAC URL และเรนเดอร์ Open Graph images แบบไดนามิกได้ทันที รองรับการสมัครสมาชิกพร้อมบัญชีฟรี (เลือกแผน Dynamic API; สร้าง API key ในแดชบอร์ด); ใช้ Free OG Image Tester เพื่อดูเทมเพลตก่อนนำไปใช้งานจริง

คู่มือฉบับสมบูรณ์สำหรับการใช้งาน HMAC-signed OG image URLs ใน Python

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

1. สร้างโมดูลตัวลงนาม (signer module)

python
# utils/fastog.py
import hmac
import hashlib
import os
from urllib.parse import quote, urlencode

def _enc(s):
    return quote(str(s), safe='-._~')  # RFC-3986: only unreserved chars

def generate_signed_og_url(params: dict, options: dict = None) -> str:
    options = options or {}
    base_url = options.get('base_url', os.environ.get('FASTOG_BASE_URL', 'https://fastog.com/api/v1/og'))
    api_key = options.get('api_key', os.environ.get('FASTOG_API_KEY'))
    hmac_secret = options.get('hmac_secret', os.environ.get('FASTOG_HMAC_SECRET'))

    if not api_key or not hmac_secret:
        raise ValueError("Missing FASTOG_API_KEY or FASTOG_HMAC_SECRET")

    all_params = {**params, 'key': api_key}
    pairs = []
    for k, v in all_params.items():
        for vv in sorted(v if isinstance(v, list) else [v]):
            pairs.append(f"{_enc(k)}={_enc(vv)}")
    canonical = '&'.join(sorted(pairs))
    signature = hmac.new(
        hmac_secret.encode(),
        f"GET/api/v1/og\n{canonical}".encode(),
        hashlib.sha256
    ).hexdigest()

    return f"{base_url}?{urlencode({**all_params, 's': signature})}"

2. ตัวแปรสภาพแวดล้อม (Environment variables)

bash
# .env
FASTOG_API_KEY=sk_your_full_api_key_here
FASTOG_HMAC_SECRET=your_64_char_hex_secret_here
bash
pip install python-dotenv
python
# config.py
from dotenv import load_dotenv
load_dotenv()

Django

บริการ Signer

python
# services/fastog.py
from utils.fastog import generate_signed_og_url

class QuickogService:
    @staticmethod
    def get_og_url(template: str, **params) -> str:
        return generate_signed_og_url({
            'template': template,
            **params,
        })

View

python
# views.py
from django.shortcuts import render
from services.fastog import QuickogService

def blog_post(request, slug):
    post = get_post(slug)

    og_image_url = QuickogService.get_og_url(
        template='blog',
        title=post.title,
        subtitle=post.excerpt,
        author=post.author.name,
    )

    return render(request, 'blog/post.html', {
        'post': post,
        'og_image_url': og_image_url,
    })

เทมเพลต

django
{# templates/blog/post.html #}
<!DOCTYPE html>
<html>
<head>
    <meta property="og:image" content="{{ og_image_url }}" />
    <meta property="og:title" content="{{ post.title }}" />
    <meta property="og:description" content="{{ post.excerpt }}" />
</head>
<body>
    <h1>{{ post.title }}</h1>
    <p>{{ post.excerpt }}</p>
</body>
</html>

แท็กเทมเพลตแบบกำหนดเอง

python
# templatetags/fastog_tags.py
from django import template
from services.fastog import QuickogService

register = template.Library()

@register.simple_tag
def og_image_url(template_name, **params):
    return QuickogService.get_og_url(template_name, **params)
django
{# In template #}
{% load fastog_tags %}
<meta property="og:image" content="{% og_image_url 'blog' title=post.title subtitle=post.excerpt %}" />

FastAPI

python
# main.py
from fastapi import FastAPI, Query
from pydantic import BaseModel
from utils.fastog import generate_signed_og_url

app = FastAPI()

class OgRequest(BaseModel):
    title: str
    template: str = "default"
    subtitle: str | None = None

@app.get("/api/og")
def generate_og(title: str = Query(...), template: str = Query("default")):
    og_url = generate_signed_og_url({
        'template': template,
        'title': title,
    })
    return {"ogImageUrl": og_url}

@app.get("/blog/{slug}")
async def blog_post(slug: str):
    post = await get_post(slug)

    og_url = generate_signed_og_url({
        'template': 'blog',
        'title': post.title,
        'subtitle': post.excerpt,
    })

    return {"post": post, "ogImageUrl": og_url}

Flask

python
# app.py
from flask import Flask, render_template, request, jsonify
from utils.fastog import generate_signed_og_url

app = Flask(__name__)

@app.route("/blog/<slug>")
def blog_post(slug):
    post = get_post(slug)

    og_image_url = generate_signed_og_url({
        'template': 'blog',
        'title': post['title'],
        'subtitle': post['excerpt'],
    })

    return render_template('blog/post.html', post=post, og_image_url=og_image_url)

@app.route("/api/og")
def api_og():
    title = request.args.get('title')
    template = request.args.get('template', 'default')

    if not title:
        return jsonify({'error': 'Missing title'}), 400

    og_url = generate_signed_og_url({
        'template': template,
        'title': title,
    })

    return jsonify({'ogImageUrl': og_url})

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

python
# views.py
def product_detail(request, product_id):
    product = get_product(product_id)

    og_image_url = generate_signed_og_url({
        'template': 'ecommerce',
        'title': product.name,
        'price': product.formatted_price,
        'image': product.image_url,
    })

    return render(request, 'products/detail.html', {
        'product': product,
        'og_image_url': og_image_url,
    })

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

python
def user_profile(request, username):
    user = get_user(username)

    og_image_url = generate_signed_og_url({
        'template': 'blog',
        'title': user.name,
        'subtitle': user.bio,
        'author': user.name,
    })

    return render(request, 'profile/show.html', {
        'user': user,
        'og_image_url': og_image_url,
    })

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

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

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

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

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

การลืมเข้ารหัส URL สำหรับคีย์และค่า

❌ ผิด: สร้างคิวรีตามรูปแบบบัญญัติด้วยค่าดิบที่ไม่ผ่านการเข้ารหัส ✅ ถูก: เข้ารหัสเปอร์เซ็นต์ทั้งคีย์และค่าด้วย RFC-3986 (safe='-._~') ก่อนเซ็น — ใช้ quote ไม่ใช่ quote_plus (ซึ่งสร้าง + สำหรับช่องว่างและจะไม่ตรงกับเซิร์ฟเวอร์)

401 missing_signature / invalid_signature

missing_signature (401) หมายความว่า key หรือ s หายไปจาก URL invalid_signature (401) หมายความว่าคิวรีตามรูปแบบบัญญัติไม่ตรงกัน — ตรวจสอบตัวคั่นบรรทัดใหม่ในข้อความที่เซ็น, hmac_secret ที่เหมือนกันทั้งสองฝั่ง, และ quote(safe='-._~') (ไม่ใช่ quote_plus) สำหรับทุกคีย์และค่า

เอกสารอ้างอิง API ฉบับสมบูรณ์

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

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

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

A BeCaSimulations site