2

所以让我们从一些背景开始。我有一个 3 层系统,在 django 中实现了一个 API,在 Apache2 服务器上运行mod_wsgi 。

今天我决定将运行在DigitalOcean的服务器从 Ubuntu 12.04 升级到 Ubuntu 14.04。没什么特别的,只是 Apache2 也更新到了2.4.7版本。在浪费了一天的大部分时间弄清楚他们实际上将默认文件夹从 /var/www 更改为 /var/www/html,破坏了功能之后,我决定测试我的 API。没有接触任何一行代码,我的一些功能就无法正常工作。

我将使用其中一个较小的函数作为示例:

# Returns the location information for the specified animal, within the specified period.
@csrf_exempt # Prevents Cross Site Request Forgery errors.
def get_animal_location_reports_in_time_frame(request):
    start_date = request.META.get('HTTP_START_DATE')
    end_date = request.META.get('HTTP_END_DATE')
    reports = ur_animal_location_reports.objects.select_related('species').filter(date__range=(start_date, end_date), species__localizable=True).order_by('-date')
    # Filter by animal if parameter sent.
    if request.META.get('HTTP_SPECIES') is not None:
        reports = reports.filter(species=request.META.get('HTTP_SPECIES'))

    # Add each information to the result object.
    response = []
    for rep in reports:
        response.append(dict(
            ID=rep.id,
            Species=rep.species.ai_species_species,
            Species_slug=rep.species.ai_species_species_slug,
            Date=str(rep.date),
            Lat=rep.latitude,
            Lon=rep.longitude,
            Verified=(rep.tracker is not None),
        ))
    # Return the object as a JSON string.
    return HttpResponse(json.dumps(response, indent = 4))

经过一些调试,我观察到request.META.get('HTTP_START_DATE')request.META.get('HTTP_END_DATE')返回None. 我尝试了许多客户端,从 REST 客户端(例如 PyCharm 和RestConsole for Chrome中的客户端)到通常与 API 通信的 Android 应用程序,但结果是相同的,这两个参数没有被发送。

然后我决定测试是否正在发送其他参数,令我恐惧的是,它们确实如此。在上面的函数中,request.META.get('HTTP_SPECIES')会有正确的值。

在对名称进行了一番摆弄之后,我观察到所有在标题中有_字符的参数都不会进入 API。

所以我想,很酷,我只用-代替_,应该可以的,对吧?错误的。-_! _

此时我完全迷惑了,所以我决定找出罪魁祸首。我使用 django 开发服务器运行 API,通过运行:

sudo python manage.py runserver 0.0.0.0:8000

当使用相同的客户端发送相同的参数时,它们会被 API 很好地拾取!因此,django 没有导致这个,Ubuntu 14.04 没有导致这个,唯一可能导致它的是 Apache 2.4.7!

现在将默认文件夹从 移动/var/www/var/www/html,从而破坏了功能,所有(在我看来)非常愚蠢的理由已经够糟糕了,但这太过分了。

有谁知道这里实际发生了什么以及为什么?

4

2 回答 2

3

这是 Apache 2.4 中的一个变化。

这是来自Apache HTTP Server 文档版本 2.4

MOD CGI, MOD INCLUDE, MOD ISAPI, ... 将标头转换为环境变量比以前更严格,以通过标头注入减轻一些可能的跨站点脚本攻击。包含无效字符 (包括下划线) 的标题现在被静默删除。Apache 中的环境变量(第 81 页)有一些关于如何解决需要此类标头的损坏的旧客户端的指示。(这会影响所有使用这些环境变量的模块。)

– 第 11 页

出于可移植性的原因,环境变量的名称可能只包含字母、数字和下划线字符。此外,第一个字符可能不是数字。传递给 CGI 脚本和 SSI 页面时,不符合此限制的字符将被下划线替换。

– 第 86 页

换句话说,这是一个非常显着的变化。所以你需要重写你的应用程序,所以发送破折号而不是下划线,而 Apache 会反过来代替下划线。

编辑

似乎有办法解决这个问题。如果您在 apache.org 上查看此文档,您会发现可以.htaccess通过将您的值foo_bar放入一个名为的新变量中来修复它,而该变量又将由 Apachefoo-bar转回。foo_bar请参见下面的示例:

SetEnvIfNoCase ^foo.bar$ ^(.*)$ fix_accept_encoding=$1
RequestHeader set foo-bar %{fix_accept_encoding}e env=fix_accept_encoding

唯一的缺点是您必须为每个标头制定规则,但您不必对客户端或服务器端的代码进行任何更改。

于 2014-08-07T14:10:26.553 回答
0

你确定 Django 也没有升级吗?

https://docs.djangoproject.com/en/dev/ref/request-response/

除了上面给出的 CONTENT_LENGTH 和 CONTENT_TYPE 之外,请求中的任何 HTTP 标头都通过将所有字符转换为大写、用下划线替换任何连字符并在名称中添加 HTTP_ 前缀来转换为 META 键。因此,例如,名为 X-Bender 的标头将映射到 META 键 HTTP_X_BENDER。

关键位是: Django 正在将“-”转换为下划线,并在其前面加上“HTTP_”。如果您在调用api 时已经添加了 HTTP_ 前缀,它可能会加倍。例如'HTTP_HTTP_SPECIES'

于 2014-07-22T17:28:16.653 回答