7

FastAPI/Starlette支持静态文件支持SPA

 2 years ago
source link: https://blog.est.im/2021/stdout-023
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

FastAPI/Starlette支持静态文件支持SPA

Posted 2021-12-08 | stdout

FastAPI 官方支持 from fastapi.staticfiles import StaticFiles 充当一个静态文件服务器

其实实现是 starlette。这玩意可以在 directory 下放一个 404.html,恰好单页应用也需要用 index.html 充当所有 javascript 框架注册的 router

只是有一个毛病,这货返回的 HTTP status code 是 404。用起来没啥大毛病,但是就是浏览器不会记录网址,导致没法匹配浏览历史快速找到之前访问过的页面。

拿来改改继续用

from starlette.staticfiles import StaticFiles, Scope, Headers, Response, FileResponse

class StaticFilesWithout404(StaticFiles):
    async def get_response(self, path: str, scope: Scope) -> Response:
        r = await super().get_response(path, scope)
        h = Headers(scope=scope)
        full_path, stat_result = await self.lookup_path('404.html')
        if isinstance(r, FileResponse) and r.path == full_path:
            if 'text/html' in h.get('accept') or '':
                r.status_code = 200
            else:
                return Response('', status_code=404)
        return r

app.mount("/frontend", StaticFilesWithout404(directory="frontend", html=True), name="static")

这段代码大概是起到了类似 nginx try_files 的作用。默认静态文件映射一个目录,但是如果找不到就按根目录的 index.html 输出。这里还判断了请求的 accept 头,如果是 xhr/Fetch 就会直接返回一个0字节长度无MIME的404。

由此我想到了一个学究式的 leaky abstraction。众所周知,如果一个URL不存在,那么服务器应该返回404

但是现在都是 SPA 单页应用,都是先 200 返回 index.html 的内容,再由 javascript 的 router 去决定是否正常显示还是404 。所以返回 200 但是显示 404 就破坏了这个语义。如果要精确匹配SPA里的router如果不存在由服务器返回404,第一是搞 SSR,把前端那一坨代码跑在服务器端,第二种办法是 npm build 的时候输出一个可路由URL列表,然后部署的时候导入静态文件服务器更新。这个具体实现就留作homework由读者自行解决了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK