ミドルウェア
ミドルウェアにより、リクエストとレスポンスをインターセプトし、ページやエンドポイントがレンダリングされる直前に動的に振る舞いを注入できます。
また、すべてのAstroコンポーネントとAPIエンドポイントで利用可能なlocals
オブジェクトを変更し、リクエスト固有の情報を各エンドポイントとページで設定・共有することもできます。
ミドルウェアはSSGとSSRのAstroプロジェクトの両方で利用可能です。
プリレンダリングされるページでは、ミドルウェアは静的なページを生成するビルドプロセスでのみ実行され、デプロイ先のウェブサイトでは実行されません。これはすべてのstatic
(SSG)プロジェクトのページに当てはまります。これにはさらに、hybrid
モードのデフォルト、またはserver
モードでexport const prerender = true
を使用した際においてプリレンダリングされるページも含まれます。
server
モードのデフォルト、またはhybrid
モードでexport const prerender = false
を使用した際においてオンデマンドにレンダリングされるページの場合、ルートへのリクエスト時にデプロイ先のウェブサイトでミドルウェアが実行されます。
基本的な使い方
セクションタイトル: 基本的な使い方-
src/middleware.js|ts
というファイルを作成します。(あるいは、src/middleware/index.js|ts
を作成しても構いません。) -
このファイルの中で、
context
オブジェクトとnext()
関数を受け取るonRequest()
関数をエクスポートします。これをデフォルトエクスポートにしてはいけません。src/middleware.js export function onRequest ({ locals, request }, next) {// リクエストからデータをインターセプトします// 必要に応じて、`locals`内のプロパティを改変しますlocals.title = "新しいタイトル";// Responseか`next()`の結果を返しますreturn next();}; -
.astro
ファイルの中で、Astro.locals
を使ってレスポンスデータにアクセスします。src/components/Component.astro ---const data = Astro.locals;---<h1>{data.title}</h1><p>この{data.property}はミドルウェアで設定しました。</p>
context
オブジェクト
セクションタイトル: contextオブジェクトcontext
オブジェクトには、レンダリング中に他のミドルウェア、APIルート、.astro
ルートで利用可能な情報が含まれています。
これはonRequest()
に渡されるオプション引数で、locals
オブジェクトや、レンダリング中に共有されるその他のプロパティを含む場合があります。たとえばcontext
オブジェクトには、認証に使用されるクッキーを含められます。
context.locals
にデータを保存する
セクションタイトル: context.localsにデータを保存するcontext.locals
は、ミドルウェア内で変更可能なオブジェクトです。
このlocals
オブジェクトは、リクエスト処理のプロセスを通じて受け渡されていき、APIContext
とAstroGlobal
のプロパティとして利用できます。これにより、ミドルウェア、APIルート、.astro
ページ間でデータを共有できます。ユーザーデータなど、リクエスト固有のデータを各レンダリングステップをまたいで保持する際に役立ちます。
インテグレーションは、locals
オブジェクトを介してプロパティを設定し、機能を提供する場合があります。インテグレーションを使用している場合は、そのドキュメントを確認して、プロパティを上書きしていないか、不要な操作をしていないかを確認してください。
locals
には、文字列、数値、さらには関数やマップといった複雑なデータ型など、どんな型のデータでも格納できます。
export function onRequest ({ locals, request }, next) { // リクエストからデータをインターセプトします // 必要に応じて、`locals`内のプロパティを改変します locals.user.name = "John Wick"; locals.welcomeTitle = () => { return "おかえりなさい " + locals.user.name; };
// Responseか`next()`の結果を返します return next();};
そして、任意の.astro
ファイル内でAstro.locals
によりこの情報を利用できます。
---const title = Astro.locals.welcomeTitle();const orders = Array.from(Astro.locals.orders.entries());---<h1>{title}</h1><p>この{data.property}はミドルウェアで設定しました。</p><ul> {orders.map(order => { return <li>{/* 各値を使って何かします */}</li>; })}</ul>
locals
は単一のAstroルートの中で生成・消滅します。ページルートがレンダリングされると、locals
はもう存在せず、その後また新しいものが作成されます。複数のページリクエストをまたいで保持されるべき情報は、別の場所に保存する必要があります。
locals
の値はランタイムに上書きできません。これをおこなうと、ユーザーが保存した情報がすべて消去される可能性があるためです。dev
モードにおいてAstroはこれを監視し、locals
が上書きされた場合にエラーをスローします。
センシティブな情報を消去する例
セクションタイトル: センシティブな情報を消去する例以下の例では、ミドルウェアを使用して「極秘情報」という文字列を「削除済み」という語に置き換えることで、変更されたHTMLをページにレンダリングできるようにします。
export const onRequest = async (context, next) => { const response = await next(); const html = await response.text(); const redactedHtml = html.replaceAll("極秘情報", "削除済み");
return new Response(redactedHtml, { status: 200, headers: response.headers });};
ミドルウェアの型
セクションタイトル: ミドルウェアの型defineMiddleware()
ユーティリティ関数をインポートして使用すると、型安全性を確保できます。
import { defineMiddleware } from "astro:middleware";
// `context`と`next`は自動的に型付けされますexport const onRequest = defineMiddleware((context, next) => {
});
JsDocにより型を記述している場合は、MiddlewareHandler
を使用できます。
/** * @type {import("astro").MiddlewareHandler} */// `context`と`next`は自動的に型付けされますexport const onRequest = (context, next) => {
};
Astro.locals
内の情報に型を付け、.astro
ファイルとミドルウェアの両コードで自動補完を有効化するには、env.d.ts
ファイルでグローバル名前空間を宣言します。
/// <reference types="astro/client" />declare namespace App { interface Locals { user: { name: string }, welcomeTitle: () => string, orders: Map<string, object> }}
これにより、ミドルウェアファイル内で自動補完が有効になり、型安全性が確保されます。
ミドルウェアを連結する
セクションタイトル: ミドルウェアを連結するsequence()
を使用して、複数のミドルウェアを指定した順序で連結できます。
import { sequence } from "astro:middleware";
async function validation(_, next) { console.log("validationリクエスト"); const response = await next(); console.log("validationレスポンス"); return response;}
async function auth(_, next) { console.log("authリクエスト"); const response = await next(); console.log("authレスポンス"); return response;}
async function greeting(_, next) { console.log("greetingリクエスト"); const response = await next(); console.log("greetingレスポンス"); return response;}
export const onRequest = sequence(validation, auth, greeting);
これにより、以下の順序でコンソールに出力されます。
validationリクエストauthリクエストgreetingリクエストgreetingレスポンスauthレスポンスvalidationレスポンス