静的サイトのURLとファイル配信
各教材の直接URL、共有アセット、404、配信設定をファイルに対応付け、前の教材を開かなくても開始できる構成を確認します。
説明
静的サイトでは、あらかじめ作ったHTML・JavaScript・データファイルを配信します。利用者ごとにHTMLを生成するサーバー処理がなくても、ブラウザ内のJavaScriptで操作できます。URLからどのファイルへ届くかを確認することが、独立した教材の入口を作る基本になります。
教材aとb、一覧、共有JavaScript、404ページを持つ小さな成果物を調べます。ローカルHTTPで各URLを直接取得し、Cloudflare Pages向けのヘッダー設定ファイルも読みます。ここで確認するHTTP応答はPythonの静的サーバーによるものです。
各教材の直接URL、共有アセット、404、配信設定をファイルに対応付け、前の教材を開かなくても開始できる構成を確認します。
1成果物を一覧にする
siteが配信するディレクトリです。ソースコードの置場と、ブラウザへ渡す成果物の範囲を分けて確認します。
python3 -c配信ディレクトリ内のファイルを列挙する 'from pathlib import Path; print("\n".join(str(p.relative_to("site")) for p in sorted(Path("site").rglob("*")) if p.is_file()))'教材ごとのindex.html、共有assets、404.html、データ、_headersがあります。これらはファイルとして保存されています。
2一覧のリンクを読む
一覧は教材への入口です。target=_blankで各教材を新しいタブに開きます。
cat site/index.htmlホームの教材リンクを読むaとbのURLへ直接リンクしています。教材内部の進捗を使ってリンク先を決める構成ではありません。
3aのURLへ直接アクセスする
static.pyは一時的なloopbackサーバーを起動してURLを取得し、statusと本文を表示します。
python3 static.py /learn/a/aの直接URLをHTTPで取得する200とaのHTMLが返ります。HTML自身に学ぶ内容の説明と共有JavaScriptの参照があるため、ホームを経由しなくても入口になります。
4bも別の入口から開始する
同じ方法でbを取得します。aの状態や履歴は渡しません。
python3 static.py /learn/b/bの直接URLをHTTPで取得するbの説明と同じ共有アセット参照が返ります。画面の共通処理は共有しても、教材の内容と開始状態は独立させられます。
5参照されたJavaScriptを取得する
HTMLが参照する絶対パス/assets/lab.v1.jsを取得します。入れ子のURLでも同じ場所を指します。
python3 static.py /assets/lab.v1.jsHTML内の参照先をHTTPで取得する応答の本文は#stateをreadyへ変えるJavaScriptです。HTML取得と、HTMLが参照するアセットの取得は別の要求です。
6存在しない教材の応答
存在しないパスでは404のstatusと案内用HTMLを返します。
python3 static.py /learn/missing/存在しないURLのstatusと本文を読む404と「指定した教材は見つかりません」が返ります。案内文だけでなくstatusも確認します。Cloudflare Pagesでは404.htmlの有無がフォールバックの挙動にも関係します。
7配信設定を読む
_headersはCloudflare Pages向けの設定ファイルです。教材HTMLは再検証し、版を名前に含めたassetsは長く保存する設定例です。
cat site/_headersパスごとのCache-Control設定を読む
cat site/data.json教材を識別する静的データを読む/learn/*と/assets/*で指定が異なります。設定ファイルを読んだことと、配置先がその設定を反映したことは別なので、配置時にはHTTPヘッダーを確認します。
まとめ
HTMLと参照アセットは別々のHTTP要求で取得されます。
直接URLに必要な説明と初期状態を含めると、教材を独立して開始できます。
404の本文とstatus、配信設定と実際の応答をそれぞれ確認します。
操作履歴と解説を振り返れます。
Python HTTP server / 静的ファイル
python3 -c 'from pathlib import Path; print("\n".join(str(p.relative_to("site")) for p in sorted(Path("site").rglob("*")) if p.is_file()))'404.html _headers assets/lab.v1.js assets/lab.v2.js data.json index.html learn/a/index.html learn/b/index.html
cat site/index.html<!doctype html><html lang="ja"><meta charset="utf-8"><title>教材一覧</title><details open><summary>数の表現</summary><a href="/learn/a/" target="_blank" rel="noopener">整数の範囲</a><br><a href="/learn/b/" target="_blank" rel="noopener">文字のバイト数</a></details></html>
python3 static.py /learn/a/status=200 content_type=text/html <!doctype html><html lang="ja"><meta charset="utf-8"><title>整数の範囲</title><h1>整数の範囲</h1><p>8ビットで表せる符号なし整数の範囲を確認します。</p><pre id="state"></pre><script src="/assets/lab.v1.js" defer></script></html>
python3 static.py /learn/b/status=200 content_type=text/html <!doctype html><html lang="ja"><meta charset="utf-8"><title>文字のバイト数</title><h1>文字のバイト数</h1><p>UTF-8で文字を保存するときのバイト数を確認します。</p><pre id="state"></pre><script src="/assets/lab.v1.js" defer></script></html>
python3 static.py /assets/lab.v1.jsstatus=200 content_type=text/javascript
document.querySelector('#state').textContent = 'ready';python3 static.py /learn/missing/status=404 content_type=text/html; charset=utf-8 <!doctype html><html lang="ja"><meta charset="utf-8"><title>見つかりません</title><h1>指定した教材は見つかりません</h1></html>
cat site/_headers
cat site/data.json/learn/*
Cache-Control: no-cache
/assets/*
Cache-Control: public, max-age=31536000, immutable
{"modules":[{"id":"a","title":"整数の範囲"},{"id":"b","title":"文字のバイト数"}]}演習完了