为什么django从线路中忽略HTTP_X_FORWARDED_PROTO,但在测试中却没有呢?

Pet*_*ans 3 python django https django-rest-framework

如果django通过网络传输,为什么它会忽略HTTP_X_FORWARDED_PROTO?

我将以下配置添加到settings.xml中:

# make sure we know when we are secure when we are behind a proxy
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
Run Code Online (Sandbox Code Playgroud)

我测试了一下

def testHttpSupport(self):
    url = reverse('configuration-list')
    response = self.client.get(url, HTTP_X_FORWARDED_PROTO='https')
    cfg = response.data[0]
    cfg_url = cfg['url']
    self.assertTrue(cfg_url.startswith('https'))
Run Code Online (Sandbox Code Playgroud)

这很好。返回对象的URL以https开头。

但是,如果我尝试:

curl -v -H 'HTTP_X_FORWARDED_PROTO: https' http://localhost:8000/api/users/
...
> GET /api/users/ HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/7.51.0
> Accept: */*
> HTTP_X_FORWARDED_PROTO: https
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Date: Mon, 03 Jul 2017 16:22:04 GMT
< Server: WSGIServer/0.2 CPython/3.6.1
< Content-Type: application/json
< Allow: GET, POST, OPTIONS
< Vary: Accept, Cookie
< X-Frame-Options: SAMEORIGIN
< Content-Length: 197 
<
* Curl_http_done: called premature == 0
* Closing connection 0
[{"url":"http://localhost:8000/api/users/1/",...
Run Code Online (Sandbox Code Playgroud)

为什么它不会像我的单元测试中那样返回基于“ https://”的URL?

knb*_*nbk 5

问题是标题名称。通过WSGI服务器访问Django时,应使用X-Forwarded-Proto标头而不是HTTP_X_FORWARDED_PROTO

curl -v -H 'X-Forwarded-Proto: https' http://localhost:8000/api/users/
Run Code Online (Sandbox Code Playgroud)

WSGI协议指出必须遵循相关的CGI规范,即:

如果使用的协议是HTTP,则名称以“ HTTP_”开头的元变量包含从客户端请求标头字段读取的值。HTTP标头字段名称被转换为大写字母,所有出现的“-”都替换为“ _”,并且以“ HTTP_”作为前缀给出了元变量名称。

来源

因此,无论何时使用WSGI服务器,X-Forwarded-Proto标题都将自动转换为,HTTP_X_FORWARDED_PROTO然后再传递给Django。HTTP_X_FORWARDED_PROTO相反,当您传递标题时,HTTP_必须仍然根据规范进行添加。因此,您最终得到一个以HTTP_HTTP_X_FORWARDED_PROTODjango 命名的标头。

self.client不是WSGI服务器,并且通过kwarg传递的值无需任何处理即可直接插入WSGI环境。因此,在这种情况下,您必须自己进行转换并实际使用HTTP_X_FORWARDED_PROTO密钥:

CGI规范

通过** extra发送的标头应遵循CGI规范。例如,模拟从浏览器到服务器的HTTP请求中发送的不同“主机”标头应作为HTTP_HOST传递。

来源