CSR・SPA・フロントエンド連携¶
このページのAPIには0.6.0以降が必要です。0.5.0にはmount_spa・mount_static・
ViteManifestはありません。python -m pip install -U higumaで更新してください。
サンプルの実行・開発にはrepositoryを取得し、rootでeditable installします。
どの方式を使うか¶
| 方式 | HTMLと画面の担当 | Higumaの使い方 |
|---|---|---|
| SSR | Python viewとMiniJinjaでHTML生成 | app.render_template() |
| CSR / SPA | 最初のHTMLを配信後、browser上でJSが画面生成・遷移 | app.mount_spa()+API |
| SSR HTML+Vite assets | PythonでHTML生成し、bundleをbrowserで実行 | ViteManifest+mount_static() |
| 静的生成 / MPA | 外部builderで複数HTMLを生成 | mount_static(..., html=True) |
React・Vue・Svelte等のコンパイラ、Node runtime、React component SSRは内蔵しません。 それぞれのbuilderが出力したHTML/JS/CSSを配信します。認証・DB・APIはHiguma側で実装します。
追加依存なしでCSRを試す¶
http://127.0.0.1:8000/app/を開きます。「APIステータス」を押すとGET /api/statusの
実JSON応答が表示されます。/app/statusを直接開く・再読み込みする・戻る操作にも対応します。
examples/frontend/は標準ES module・History API・fetchのみで動くサンプルです。
ビルド済みSPAを配信する¶
from higuma import Higuma
app = Higuma(__name__, static_folder=None)
@app.get("/api/hello")
def hello():
return {"message": "hello"}
app.mount_spa("/", "frontend/dist")
指定directoryにindex.htmlが必要です。directory/indexの欠落は登録時にValueErrorとなり、
空の配信設定で起動することを防ぎます。相対directoryはプロセスの作業directory基準です。
application module基準にしたい場合はPath(__file__).parent / "frontend/dist"等を渡します。
サブパスでは次のようにします。
frontendのasset baseとrouter basenameも/app/へ揃えます。Viteならbase: '/app/'を設定して
再ビルドします。React RouterのbasenameやVue Routerのhistory baseもfrontend側の責任です。
絶対パス/assets/...を生成したbundleを、そのまま/app/配下へ移してもURLは変わりません。
history fallbackの条件¶
- GET/HEADだけを受け付け、通常の明示routeを優先します。既知のPOST専用routeへのGETは405です。
- 実在する公開fileはそのまま配信します。directory listingは行いません。
- 未存在の拡張子なしURLはHTML shellへfallbackします。dotを含むpath segmentはfallbackしません。
api_prefixes=("/api",)は予約領域です。asset_prefixes=("/assets",)では実在fileのみ配信し、 拡張子のない欠損assetもHTMLにしません。どちらもmount内の相対prefixで、segment境界で照合します。- AcceptがHTMLを拒否するrequest(例:
application/json、text/html;q=0)はfallbackしません。 Accept未指定・*/*は許可します。APIは必ず予約prefixまたは明示routeへ分けてください。 - アプリが返した404/401/403/500を後からHTMLに置き換える処理ではありません。
- dotfile、dot segment、backslash、drive/ADS構文、root外へのsymlinkは配信しません。
app.mount_spa(
"/app", "frontend/dist", name="client",
api_prefixes=("/api", "/graphql"),
asset_prefixes=("/assets", "/fonts", "/images"),
)
同じGET pathやendpoint名が衝突すると登録前にValueErrorになります。
複数mountではnameを変えます。url_for("client_root")と
url_for("client_path", filename="assets/main.js")で配信URLを生成できます。
複数static rootと静的HTML¶
app.mount_static("/media", "public/media", name="media", max_age=3600)
app.mount_static("/help", "public/help", name="help", html=True)
app.mount_static("/assets", "frontend/dist/assets", name="bundles",
max_age=31536000, immutable=True)
html=Trueではdirectory内のindex.htmlを配信し、末尾slashのないdirectory URLは308で
slash付きへredirectします。query stringは維持します。html=Falseではdirectoryは404です。
index_fileで別の可視filenameを指定できます。隠しfileやroot外へのsymlinkは常に拒否します。
immutable=TrueはURLが内容に応じて変わるhash付きasset専用にしてください。同じURLを上書きする
画像等に長期immutableを付けると、更新が利用者へ届きません。HTML・sw.js・service-worker.jsの
既定cacheはno-cacheです。SPA shellは常にno-cache+Vary: Acceptになります。
その他のfileは既定public, max-age=3600、X-Content-Type-Options: nosniffを付けます。
file bodyはRustからstreamされます。全fileのFileResponseとsend_file()も含め、
GET/HEADで次の条件付き配信を共通実装します。
| Request header | 動作 |
|---|---|
If-None-Match |
ETag list・weak comparison・*に対応し、未変更なら304 |
If-Modified-Since |
未更新なら304。If-None-Matchがあればそちらを優先 |
If-Match |
strong comparison。不一致なら412 |
If-Unmodified-Since |
更新済みなら412。If-Matchがあればそちらを優先 |
Range: bytes=... |
single rangeは206、不成立・複数rangeは416 |
If-Range |
現在のstrong ETagまたはLast-Modified日時と一致した場合だけ206。不一致・weak値なら全体200 |
Last-ModifiedとETagはfile metadataから生成します。ETagは暗号学的な内容署名ではありません。
書込みAPIの同時更新制御はこの配信処理とは別に実装します。
Vite:開発時と本番¶
開発時¶
Vite dev serverでfrontendを開き、APIをHigumaへproxyする構成が簡単です。 既存Vite projectの設定へ統合してください。この設定自体はHigumaのPython依存を増やしません。
import { defineConfig } from 'vite';
export default defineConfig(({ command }) => ({
base: command === 'serve' ? '/' : '/app/',
server: {
host: '127.0.0.1',
proxy: { '/api': 'http://127.0.0.1:8000' },
},
build: { manifest: true },
}));
HMRはViteの機能です。HigumaがNodeを起動したり、ViteのWebSocketを代理したりする機能ではありません。
開発時はfrontendのlocal URLのroot、本番はHigumaの/app/へアクセスします。
router baseはimport.meta.env.BASE_URLを使う等で両方に合わせます。この例は開発時のbaseをrootにして
/api proxyと揃えています。非root baseのまま開発する場合はViteのproxy規則
に従い、proxy keyとfrontendのAPI URLにもbaseを反映してください。dev serverを無制限の
CORSで本番公開しないでください。通常のReact/Vue SPAはbuildしたindex.htmlをそのままmountでき、
次のmanifest helperは必須ではありません。
PythonでHTMLを生成する場合¶
Viteでbuild.manifest: trueを指定すると.vite/manifest.jsonが生成されます。
Python側でそのsnapshotを読み、entryと依存CSS/JSのURLを解決できます。
from higuma import ViteManifest
vite = ViteManifest("frontend/dist/.vite/manifest.json", base_url="/build/")
logo_url = vite.asset_url("src/logo.svg") # manifestに存在するkeyを指定
tags = vite.tags("index.html", nonce="request-specific-csp-nonce")
base_urlとViteのbaseを/build/へ揃え、app.mount_static("/build", "frontend/dist")で配信します。
examples/vite_ssr.pyはHIGUMA_FRONTEND_DIST(既定frontend/dist)から自分のVite buildを読みます。
SSRするのはPython側のHTMLであり、React componentをserverで実行する例ではありません。
asset_url(entry)は単一の出力URLを返します。未知keyはKeyErrorです。entrypoint(entry)はfile、css、importsのURLを返します。tags(entry, nonce=None)はentry CSS→依存CSS→module script→modulepreloadの順でHTMLを生成します。 同じCSS/importは重複せず、循環importで無限反復しません。CSS-only entryも扱います。- dynamic importは先読みせず、bundleの実行時importに任せます。画像等は
asset_url()で扱います。 - URLとnonceはHTML escapeします。相対file以外のmanifest出力や危険なbase URLは拒否します。
- 読込は起動時/明示
reload()時のみです。不正snapshotへのreload失敗時は以前のsnapshotを維持します。 buildの入替えはatomicに行い、古いhash assetも既存タブの利用期間は保持してください。 - Node rendering、React Refresh preamble、experimental chunk import mapの自動挿入は提供しません。
Cookie認証・CORS・本番運用¶
CSRでもsessionとCSRFの責任は変わりません。Cookie認証で更新APIを呼ぶ場合は、
最初のGETでCSRF tokenを取得し、POST/PATCH/DELETEへX-CSRF-Tokenを送ります。
token・OAuth client secret・DB credentialsをJS bundleやpublic directoryへ入れないでください。
別originから呼ぶ場合はCORSMiddlewareで許可originを明示し、必要な場合だけcredentialsを許可します。
browserのfetchにはcredentials: 'include'も必要です。CORSは認証・CSRF防御の代替ではありません。
SameSite・Secure Cookie・HTTPS・trusted Host/proxy・WebSocket Originも本番に合わせます。
- ユーザー固有のAPI応答・tokenには
Cache-Control: no-storeを使い、public cacheへ混ぜない。 - public directoryはビルド出力だけにし、source、秘密情報、DB、server側upload保存先を混ぜない。
- SPA shellはルーター上の未知画面でもHTTP200です。SEOや厳密なHTTP404が必要な画面にはSSR/静的生成を使う。
- staleタブのlazy chunk欠落を避けるため、更新はatomicにし、古いassetsを一定期間維持する。
- service workerの生成・offline cache更新戦略・web manifestはfrontendで管理する。Higumaが自動PWA化するわけではない。
- precompressed Brotli asset選択、CDN purge、分散cache・job queueは外部proxy/serviceで構成する。
公式仕様の参照先: Vite backend integration、 React Router SPA。全体の対応範囲は機能対応表を参照してください。