edgeone-makers-middleware について
EdgeOne の V8 エッジランタイムで、非フレームワークまたは静的プロジェクト向けのリクエスト処理を実装する Skill です。ルーティングとリクエスト制御の方法を扱います。
主な機能
- 01エッジでのリクエスト処理
- 02リダイレクトとパスの書き換え
- 03matcher によるルート指定
- 04認証ガードと JSON 応答
- 05位置情報ルーティングと A/B テスト
- 06リクエストヘッダーの追加
活用シーン
- 01旧 URL のリダイレクト
- 02API や管理ルートの保護
- 03クライアントの位置によるルーティング
- 04複数のランディングページのテスト
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ tencentedgeone/ edgeone-makers-tools --skill edgeone-makers-middlewareインストール後、Claude Code で/edgeone-makers-middlewareを入力して読み込まれたか確認します。
詳細
ミドルウェア
エッジ(V8 ランタイム)で実行する軽量なリクエストインターセプトです。リダイレクト、リライト、認証ガード、A/B テスト、ヘッダーの挿入に使用します。
⚠️ フレームワークプロジェクト(Next.js、Nuxt など):このプラットフォームのミドルウェア形式は使用しないでください。代わりに、フレームワーク組み込みのミドルウェアを使用してください(例:Next.js の
middleware.tsとNextRequest/NextResponse)。以下のパターンは、フレームワークを使用しないプロジェクト、または純粋な静的プロジェクト専用です。
基本的なミドルウェア
ファイル:middleware.js(プロジェクトのルート)
export function middleware(context) {
const { request, next, redirect, rewrite } = context;
// Pass through — no modification
return next();
}
コンテキスト API
| プロパティ | 型 | 説明 |
|---|---|---|
request | Request | 現在のリクエストオブジェクト |
next(options?) | Function | オリジンに処理を続行します。必要に応じてヘッダーを変更できます |
redirect(url, status?) | Function | リダイレクトします(デフォルトは 307) |
rewrite(url) | Function | リクエストパスを書き換えます(クライアントには透過的です) |
geo | GeoProperties | クライアントの位置情報 |
clientIp | string | クライアントの IP アドレス |
ルートマッチング
デフォルトでは、ミドルウェアはすべてのルートで実行されます。対象範囲を制限するには、config.matcher を使用します。
// Only run on /api/* routes
export const config = {
matcher: ['/api/:path*'],
};
export function middleware(context) {
// Auth check for API routes only
const token = context.request.headers.get('Authorization');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
return context.next();
}
マッチャーパターン:
// Single path
export const config = { matcher: '/about' };
// Multiple paths
export const config = { matcher: ['/api/:path*', '/admin/:path*'] };
// Regex
export const config = { matcher: ['/api/.*', '^/user/\\d+$'] };
よくあるパターン
URL リダイレクト
export function middleware(context) {
const url = new URL(context.request.url);
if (url.pathname === '/old-page') {
return context.redirect('/new-page', 301);
}
return context.next();
}
URL リライト(透過プロキシ)
export function middleware(context) {
const url = new URL(context.request.url);
if (url.pathname.startsWith('/blog')) {
return context.rewrite('/content' + url.pathname);
}
return context.next();
}
リクエストヘッダーの追加
export function middleware(context) {
return context.next({
headers: {
'x-request-id': crypto.randomUUID(),
'x-client-ip': context.clientIp,
'x-country': context.geo.countryCodeAlpha2,
},
});
}
認証ガード
export const config = {
matcher: ['/api/:path*', '/admin/:path*'],
};
export function middleware(context) {
const token = context.request.headers.get('Authorization');
if (!token || !token.startsWith('Bearer ')) {
return new Response(JSON.stringify({ error: 'Unauthorized' }), {
status: 401,
headers: { 'Content-Type': 'application/json' },
});
}
return context.next();
}
地域に基づくルーティング
export function middleware(context) {
const country = context.geo.countryCodeAlpha2;
if (country === 'CN') {
return context.rewrite('/zh' + new URL(context.request.url).pathname);
}
return context.next();
}
A/B テスト
export function middleware(context) {
const url = new URL(context.request.url);
if (url.pathname === '/landing') {
const variant = Math.random() < 0.5 ? '/landing-a' : '/landing-b';
return context.rewrite(variant);
}
return context.next();
}
JSON レスポンスを直接返す
export function middleware(context) {
const url = new URL(context.request.url);
if (url.pathname === '/api/health') {
return new Response(JSON.stringify({ status: 'ok', timestamp: Date.now() }), {
headers: { 'Content-Type': 'application/json' },
});
}
return context.next();
}
GeoProperties
context.geo で利用できます。
| プロパティ | 型 | 例 |
|---|---|---|
countryName | string | Singapore |
countryCodeAlpha2 | string | SG |
countryCodeAlpha3 | string | SGP |
regionName | string | — |
cityName | string | Singapore |
latitude | number | 1.29027 |
longitude | number | 103.851959 |
asn | number | 132203 |
コメント
Markdown 対応