所以让我们从一些背景开始吧。我有一个3层系统,在Apache的服务器上用mod_wsgi运行的django中实现了API。
今天我决定将运行在DigitalOcean的服务器从Ubuntu 12.04升级到Ubuntu 14.04。没什么特别的,只有Apache2也更新到版本 2.4.7 。在浪费了当天的大部分时间来查明they actually changed the default folder from /var/www to /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')
将具有正确的值。
在对名称进行了一些摆弄之后,我发现 ALL 标题中包含_
字符的参数不会进入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
,从而打破了功能,一切(在我看来)非常愚蠢的原因已经够糟了,但这太多了。
有没有人知道这里发生了什么以及为什么?
答案 0 :(得分:3)
这是Apache 2.4的变化。
这是Apache HTTP Server Documentation Version 2.4:
MOD CGI,MOD INCLUDE,MOD ISAPI,...标题到环境变量的翻译比以前更严格 通过标头注入缓解一些可能的跨站点脚本攻击。包含无效字符的标头 (包括下划线) 现在以静默方式删除。 Apache中的环境变量(p.81)有一些指针 关于如何解决需要此类标头的损坏的旧客户端。 (这会影响所有使用的模块 这些环境变量。)
- 第11页
出于可移植性的原因,环境变量的名称可能只包含字母,数字和下划线字符。另外,第一个字符可能不是数字。 当传递给CGI脚本和SSI页面时,与此限制不匹配的字符将被下划线替换。
- Page 86
换句话说,这是一个非常重要的变化。所以你需要重写你的应用程序,所以发送破折号而不是下划线,而Apache又将替换下划线。
修改强>
这似乎有办法解决这个问题。如果您在apache.org上查看this document,可以看到您可以通过将.htaccess
的值放入名为foo_bar
的新变量中,在foo-bar
中修复它而后者又将由Apache转回foo_bar
。见下面的例子:
SetEnvIfNoCase ^foo.bar$ ^(.*)$ fix_accept_encoding=$1
RequestHeader set foo-bar %{fix_accept_encoding}e env=fix_accept_encoding
唯一的缺点是你必须为每个标题制定一个规则,但你不必对客户端或服务器端的代码进行任何更改。
答案 1 :(得分:0)
你确定Django也没有升级吗?
https://docs.djangoproject.com/en/dev/ref/request-response/
除了CONTENT_LENGTH和CONTENT_TYPE之外,如上所述,请求中的任何HTTP头都将转换为META密钥,方法是将所有字符转换为大写,用下划线替换任何连字符,并在名称中添加HTTP_前缀。因此,例如,名为X-Bender的标头将映射到META密钥HTTP_X_BENDER。
关键部分是:Django正在转换' - '强调并预先设置HTTP _'它。如果您在调用 api时已经添加了HTTP_前缀,则可能会增加一倍。例如' HTTP_HTTP_SPECIES'