客至汲泉烹茶, 抚琴听者知音

fastapi解决默认文档打不开的方案

前言

今天久违地打开了了github仓库,发现shu-s-project居然有十五个star!又引起我写代码的兴趣了,于是clone下来准备先加几个API。调试过程中突然发现fastapi自带的docs文档打不开,控制台看了一下原来是静态资源请求失败,由于众所周知的原因,jsdelivr​已经被屏蔽了,自然导致docs打开一片空白。

先在网上搜了一下解决方案,有的说直接把静态资源放到本地,有的说改fastapi源码,把CDN地址切换到其他可以访问的地址。这两种方案我都不喜欢,前者意味着可能每个项目都要搞一套静态资源,而且失去了CDN的意义(当然本地访问无所谓),后者更是下策,稍微升个级可能又失效了。Github上也有相关讨论,参见It is suggested to change the CDN of Swagger and Redoca parameter of FastAPI to alter base url of cdn.jsdelivr.net。经过考虑,我决定采用Beipy的方案,即直接在主程序中写一个函数,主动替换CDN地址,这样不用修改源码。代码如下:

from fastapi import applications
from fastapi.openapi.docs import get_swagger_ui_html


def swagger_monkey_patch(*args, **kwargs):
    """
    Wrap the function which is generating the HTML for the /docs endpoint and
    overwrite the default values for the swagger js and css.
    """
    return get_swagger_ui_html(
        *args, **kwargs,
        swagger_js_url="https://unpkg.zhimg.com/swagger-ui-dist@3.29/swagger-ui-bundle.js",
        swagger_css_url="https://unpkg.zhimg.com/swagger-ui-dist@3.29/swagger-ui.css")


# Actual monkey patch
applications.get_swagger_ui_html = swagger_monkey_patch

原理见https://fastapi.tiangolo.com/reference/openapi/docs/,docs和redoc都有专门的函数生成,其参数中有引用的CDN地址,那我们只需要把函数的参数改掉就行了。

新的问题

想必大家也注意到了,替换的js和css地址是有版本号的,事实上可能不同的fastapi版本用的是不同版本的静态资源,如果我们替换的版本号和fastapi实际使用的版本不一样会怎么样?试了一下,果然报错:

这就麻烦了,难道我每次都要去源码查看CDN版本,再手动替换?这样的话还不如用本地的方案呢。

不过办法总比困难多,既然原始的get_swagger_ui_html函数给出了引用的CDN地址,那我们直接获取这个参数值,然后替换前缀不就行了(jsdelivr​和unpkg​基本上可以一一对应,所以我们只要replace即可,这里使用unpkg​,因为要比知乎的CDN全),顺着这个思路,很快代码就写好了。

最终解决

完整代码如下:

import inspect

def get_function_default_args(func):
    '''获取函数默认参数'''
    sign = inspect.signature(func)
    return {
        k: v.default
        for k, v in sign.parameters.items()
        if v.default is not inspect.Parameter.empty
    }

def swagger_monkey_patch(*args, **kwargs):
    """
    Wrap the function which is generating the HTML for the /docs endpoint and
    overwrite the default values for the swagger js and css.
    """
    param_dict = get_function_default_args(get_swagger_ui_html)
    swagger_js_url = param_dict['swagger_js_url'].replace('https://cdn.jsdelivr.net/npm/', 'https://unpkg.com/')
    swagger_css_url = param_dict['swagger_css_url'].replace('https://cdn.jsdelivr.net/npm/', 'https://unpkg.com/')
    return get_swagger_ui_html(
        *args, **kwargs,
        swagger_js_url=swagger_js_url,
        swagger_css_url=swagger_css_url
    )

applications.get_swagger_ui_html  = swagger_monkey_patch
注: redoc也可以这么改,引入fastapi.openapi.docs.get\_redoc\_html函数替换redoc_js_url​即可

问题解决。

添加新评论