# 标头(Headers)
请求头和响应头仅在对应的 Request
对象和 HTTPResponse
对象中起作用。它们使用 multidict
包 (opens new window) 进行构建,这意味着它们允许一个键名具有多个对应值。
小提示:
请求头或响应头中的键名将会在解析过程中被转换为小写,Headers 中不考虑大写键名。
# 请求头(Request Headers)
Sanic 确实试图在把请求头提交给开发人员之前对它们进行一些规范处理,并且对常见的用例进行一些潜在的有意义的提取。
# 令牌(Tokens)
从请求头中解析到的 Token <token>
或者 Bearer <token>
将会被赋值给 request.token
。
@app.route("/")
async def handler(request):
return text(request.token)
$ curl localhost:8000 \
-H "Authorization: Token ABCDEF12345679"
ABCDEF12345679
$ curl localhost:8000 \
-H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
# 代理头(Proxy headers)
Sanic 对代理头也有着特殊的处理,具体的细节请参考 代理头 章节的解释
# 主机标头和动态URL的构建(Host header and dynamic URL construction)
您可以通过 request.host
属性来获取有效主机名。该值不一定与头信息中的主机一致,因为它更倾向于保存反向代理的主机信息,并且可以通过服务器名称强行设置。
在通常情况下,Web 应用应该去设置并使用这个属性,这样能保证在任何部署方式下都能提供同样的功能。如果需要的话 request.headers
可以获取真实的主机头信息。
有效的主机名称也可以与 request.url_for
方法一起使用,它可以确定响应函数所对应的外部地址。
警惕恶意客户端
由于头信息中的主机信息可能会被客户端恶意替换,为了生成正确的 URL,您应该考虑使用 app.url_for
方法。
app.config.SERVER_NAME = "https://example.com"
@app.route("/hosts", name="foo")
async def handler(request):
return json(
{
"effective host": request.host,
"host header": request.headers.get("host"),
"forwarded host": request.forwarded.get("host"),
"you are here": request.url_for("foo"),
}
)
$ curl localhost:8000/hosts
{
"effective host": "example.com",
"host header": "localhost:8000",
"forwarded host": null,
"you are here": "https://example.com/hosts"
}
# 其他标头(Other headers)
您可以在请求对象的 request.headers
属性中获取所有的请求头,并且可以通过字典的方式来进行访问。Headers 的键名不考虑大小写,可以通过大写或小写键名来进行访问。
@app.route("/")
async def handler(request):
return json(
{
"foo_weakref": request.headers["foo"],
"foo_get": request.headers.get("Foo"),
"foo_getone": request.headers.getone("FOO"),
"foo_getall": request.headers.getall("fOo"),
"all": list(request.headers.items()),
}
)
$ curl localhost:9999/headers -H "Foo: one" -H "FOO: two"|jq
{
"foo_weakref": "one",
"foo_get": "one",
"foo_getone": "one",
"foo_getall": [
"one",
"two"
],
"all": [
[
"host",
"localhost:9999"
],
[
"user-agent",
"curl/7.76.1"
],
[
"accept",
"*/*"
],
[
"foo",
"one"
],
[
"foo",
"two"
]
]
}
小提示
💡 request.headers 对象是少数几个字典类型之一,每个值都是一个列表。这是因为HTTP允许重用一个键来发送多个值。
大多数情况下,您会希望使用 .get()或 .getone()方法访问第一个元素,而不是列表。如果您想要所有项目的列表,您可以使用 .getall() 方法。
# Request ID
@app.route("/")
async def handler(request):
return text(request.id)
$ curl localhost:8000 \
-H "X-Request-ID: ABCDEF12345679"
ABCDEF12345679
# 响应头(Response Headers)
Sanic将为您自动设置以下响应头(如果适用):
content-length
content-type
connection
transfer-encoding
在大多数情况下,您应该不需要去关心如何去设置这些标头。
如果您想要设置其他的标头,那您可以在路由处理程序或者响应中间件中进行添加。
@app.route("/")
async def handler(request):
return text("Done.", headers={"content-language": "en-US"})
@app.middleware("response")
async def add_csp(request, response):
response.headers["content-security-policy"] = "default-src 'none'; script-src 'self'; connect-src 'self'; img-src 'self'; style-src 'self';base-uri 'self';form-action 'self'"
您可能会想要为响应也添加 X-Request-ID
头信息,通常,您可以添加一个 中间件。
如上所述。request.id
可以从请求头中获取请求 ID。并且如果在请求中没有 X-Request-ID
头,也会自动为您创建一个。
@app.route("/")
async def handler(request):
return text(str(request.id))
@app.on_response
async def add_request_id_header(request, response):
response.headers["X-Request-ID"] = request.id
$ curl localhost:8000 -i
HTTP/1.1 200 OK
X-Request-ID: 805a958e-9906-4e7a-8fe0-cbe83590431b
content-length: 36
connection: keep-alive
content-type: text/plain; charset=utf-8
805a958e-9906-4e7a-8fe0-cbe83590431b