AberSheeran
Aber Sheeran

Django静态文件访问错误

起笔自
所属文集: 程序杂记
共计 2952 个字符
落笔于

我们在开发pdk365的时候,前端开发的机子突然无法访问他本机架起的Django服务器的静态文件了。

报错就类似于下面的,但我遇见的问题不同的是,它们的根路径是一样的。

The joined path (/var/folders/t9/7v8mki3s3h39fzylxfpsk9640000nn/T/tmpmvb9wxq6) is located outside of the base path component (/Users/zorgan/Desktop/app/draft1/media)

我直接去StackOverFlow查,发现其他人遇见的问题都是没有正确指定静态文件路径。然而我们是没有这个情况的,因为在我们几个后端开发的机子上,这个代码没有问题啊!😀

当时忙于其他事,我没有深入去想,以为只是配置问题。直到今天万恶的PM又问了我一次,我才开始思考这个问题。

然后...我想起来曾经从一篇博客中看到对Django早期版本的静态文件访问系统的Bug分析,那个路径拼接Bug可以达到任意文件访问的效果。这个报错怕不是对这个Bug的修补,那么我瞅了一眼settings.py,大概猜到了为什么。去掉了最后一个'/'之后,就能正常访问了

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static/")
]

So,Why?

让我们去抛出异常的地方看一看,也就是django.utils._os.safe_join()

def safe_join(base, *paths):
    """
    Join one or more path components to the base path component intelligently.
    Return a normalized, absolute version of the final path.

    Raise ValueError if the final path isn't located inside of the base path
    component.
    """
    base = force_text(base)
    paths = [force_text(p) for p in paths]
    final_path = abspath(join(base, *paths))
    base_path = abspath(base)
    # Ensure final_path starts with base_path (using normcase to ensure we
    # don't false-negative on case insensitive operating systems like Windows),
    # further, one of the following conditions must be true:
    #  a) The next character is the path separator (to prevent conditions like
    #     safe_join("/dir", "/../d"))
    #  b) The final path must be the same as the base path.
    #  c) The base path must be the most root path (meaning either "/" or "C:\\")
    if (not normcase(final_path).startswith(normcase(base_path + sep)) and
            normcase(final_path) != normcase(base_path) and
            dirname(normcase(base_path)) != normcase(base_path)):
        raise SuspiciousFileOperation(
            'The joined path ({}) is located outside of the base path '
            'component ({})'.format(final_path, base_path))
    return final_path

先看一眼这个多次出现的函数normcase的作用

def normcase(s):
    """Normalize case of pathname.

    Makes all characters lowercase and all slashes into backslashes."""
    s = os.fspath(s)
    try:
        if isinstance(s, bytes):
            return s.replace(b'/', b'\\').lower()
        else:
            return s.replace('/', '\\').lower()
    except (TypeError, AttributeError):
        if not isinstance(s, (bytes, str)):
            raise TypeError("normcase() argument must be str or bytes, "
                            "not %r" % s.__class__.__name__) from None
        raise

把所有的'/'转为'\\'并且字母小写。

在与normcase(s)的同一个文件里,能看到

sep = '\\'

可以看出来应该是not normcase(final_path).startswith(normcase(base_path + sep))这个条件不成立。因为路径拼接在某些情况下,它不会帮你去掉多余的'\',有些会帮你去掉重复的'\',所以出现了在我们几个后端开发的电脑上正常,在前端的电脑上不正常的情况。至于这个成立原因是什么,由于PM不给我出问题的机子继续研究,暂时无法得知。

如果你觉得本文值得,不妨赏杯茶
HTML字体解码错误
Coding-WebHook