by WebSurfer
22. November 2024 13:48
開発マシンが Windows OS の場合、Flask 開発サーバーが拡張子 .mjs のファイルを応答として返す時、応答ヘッダの Content-Type が text/plain になるので (MacOS や Linux の場合は不明)、ブラウザは受信したスクリプトのロードに失敗することがありました。その理由と解決方法を備忘録として書きます。
元の話は Qiita の質問「アップロードしたファイルをPDF.jsビューワーで開きたい」で、PDF.js を Flask アプリで使うことに関する Q&A の際に自分が遭遇した問題です。
拡張子 .mjs のファイルはモジュール機能を持つ JavaScript ファイルです。<script> 要素に type="module" 属性を含めることでそのスクリプトがモジュールであることを宣言します。詳しくは MDN の記事「JavaScript モジュール」を見てください。
html コードに含まれる <script> 要素の src=".../xxx.mjs" 属性の指定による要求に応じて、Flask 開発サーバーが xxx.mjs ファイルを応答として返す時、応答ヘッダに含まれる Content-Type が text/plain になります。
その結果、下の画像のようにブラウザ (Chrome の例です) は受信した .mjs ファイルのスクリプトのロードに失敗します。
上の画像のエラーメッセージに "Strict MIME type checking is enforced for module scripts per HTML spec." とあります。ブラウザは type="module" 属性を見て module scripts であると判定し、応答ヘッダに含まれる Content-Type が text/plain では HTML spec に反するのでロードしないと言っているようです。
ブラウザがスクリプトをロードできるようにするには、応答ヘッダの Content-Type を text/javascript にする必要があります。(application/javascript は RFC9239 で廃止になったそうで、text/javascript を使えということになっています。Windows OS のレジストリや IIS の MIME Mapping では未だに .js には application/javascript が使われていますが)
なぜ Flask 開発サーバーが Content-Type を text/plain に設定するかは、stackoverflow の記事 Python Flask - Error: "Failed to load module script. Strict MIME type checking is enforced". Works on production, not on the local server に書いてありますが、Windows PC の場合レジストリの設定情報を読んでくるからだそうです。
自分の環境の Windows 10 22H2 のレジストリを調べてみると、以下の通り .mjs は text/plain になってました。ということで、諸悪の根源は Windows OS ということのようです。
なお、レジストリに .mjs の設定がないケースがあって、その場合は mimetypes 内蔵のデータベースを使って拡張子から text/javascript と推測するので問題ないことがあるようです。
自分の開発マシンでは上の画像のとおりレジストリの設定があるのですが、レジストリをいじらなくても以下の様な設定で変更可能ということが上に紹介した stackoverflow の記事書いてありました。
import mimetypes
mimetypes.add_type('test/javascript', '.mjs')
試してみると、応答ヘッダの Content-Type は text/javascript になり、ブラウザはスクリプトをロードして期待通り動くようになりました。