目录
自定义路由名称(Customizing a route name)
路由参数类型
字符串str
from sanic import Sanic
app = Sanic()
@app.route("/path/to/<foo:str>")
async def handler(request, foo:str):
pass
使用正则表达式:r"[^/]+"
转换类型:str
匹配示例 /path/to/Hah; /path/to/Python%203
v22.3新strorempty
from sanic import Sanic
app = Sanic()
@app.route("/path/to/<foo:stroempty>")
async def handler(request, foo:str):
pass
使用正则表达式: r"[^/]*")
转换类型: str
匹配示例:
/path/to/Bob
/path/to/Python%203
/path/to/
与 str 不同,strorempty 还能够匹配空字符串路径
整型int
from sanic import Sanic
app = Sanic()
@app.route("/path/to/<foo:int>")
async def handler(request, foo:int):
pass
使用正则表达式:r"-?\d+")
匹配例子
/path/to/10
/path/to/-10
无法匹配浮点型float, hex十六进制,octal八进制,etc
float 浮点型
@app.route("/path/to/<foo:float>")
async def handler(request, foo: float):
...
使用的正则表达式: r"-?(?:\d+(?:\.\d*)?|\.\d+)")
转换类型: float
匹配示例:
/path/to/10
/path/to/-10
/path/to/1.5
在之前版本中,您应该这样写 <foo:number>。这种写法将在 v21.12 中被弃用
alpha
@app.route("/path/to/<foo:alpha>")
async def handler(request, foo: str):
...
使用的正则表达式: r"[A-Za-z]+")
转换类型: str
匹配示例:
/path/to/Bob
/path/to/Python
无法匹配数字,空格以及其他特殊字符。
slug
在django中,slug指有效url的一部分,能使url更加清晰易懂,比如有这样一篇文章,标题是"13岁的孩子,它的URL地址是"/posts/13-sui-de-hai-zi",后面这一部分便是slug
@app.route("/path/to/<article:slug>")
async def handler(request, article: str):
...
使用的正则表达式: r"[a-z0-9]+(?:-[a-z0-9]+)*")
类型转换: str
匹配示例:
/path/to/some-news-story
/path/to/or-has-digits-123
path
@app.route("/path/to/<foo:path>")
async def handler(request, foo: str):
...
Copied!
使用的正则表达式: r"[^/].*?")
转换类型: str
匹配示例:
/path/to/hello
/path/to/hello.txt
/path/to/hello/world.txt
注意
因为这将从 / 开始进行匹配,所以您应该小心使用,并测试您的正则表达式是否正确,以免匹配错误而调用了错误的响应函数。
ymd 年月日
@app.route("/path/to/<foo:ymd>")
async def handler(request, foo: datetime.date):
...
使用的正则表达式: r"^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))"
转换类型: datetime.date
匹配示例:
/path/to/2021-03-28
UUID
@app.route("/path/to/<foo:uuid>")
async def handler(request, foo: UUID):
...
使用的正则表达式: r"[A-Fa-f0-9]{8}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{4}-[A-Fa-f0-9]{12}"
转换类型: UUID
匹配示例:
/path/to/123a123a-a12a-1a1a-a1a1-1a12a1a12345
regex
@app.route("/path/to/<foo:^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))>")
async def handler(request, foo: str):
使用的正则表达式: whatever you insert
转换类型: str
匹配示例:
/path/to/2021-01-01
该方法允许您使用自定义的匹配模式,在上面的示例中,我们通过指定的正则表达式,来匹配符合 YYYY-MM-DD 格式的路由参数。
ext
v22.3 新特征
@app.route("/path/to/<foo:ext>")
async def handler(request, foo: UUID):
...
Copied!
使用的正则表达式: n/a
转换类型: varies
匹配示例:
定义 示例 文件名 拓展名
<file:ext> page.txt "page" "txt"
<file:ext=jpg> cat.jpg "cat" "jpg"
<file:ext=jpg|png|gif|svg> cat.jpg "cat" "jpg"
<file=int:ext> 123.txt 123 "txt"
<file=int:ext=jpg|png|gif|svg> 123.svg 123 "svg"
<file=float:ext=tar.gz> 3.14.tar.gz 3.14 "tar.gz"
可以使用特殊的 ext 参数类型匹配文件扩展名。它使用一种特殊的格式,允许您指定其他类型的参数类型作为文件名,以及一个或多个特定的扩展名,如上表所示。
该方法 不 不支持 path 类型的参数。
动态访问
Sanic 提供了一种基于处理程序方法名生成 url 的方法:app.url_for()
,您只需要函数名称即可实现响应函数之间的处理权力的移交。在您不希望将 url 进行硬编码或希望响应函数之间具有层级关系的时候,这将非常有用。它的使用方法如下:
@app.route('/')
async def index(request):
# generate a URL for the endpoint `post_handler`
url = app.url_for('post_handler', post_id=5)
# Redirect to `/posts/5`
return redirect(url)
@app.route('/posts/<post_id>')
async def post_handler(request, post_id):
...
自定义路由名称(Customizing a route name)
在注册路由的时候,可以通过给定 name
参数来自定义路由名称
@app.get("/get", name="get_handler")
def handler(request):
return text("OK")
现在,您可以通过自定义的名称进行路由匹配。
>> > app.url_for("get_handler", foo="bar")
'/get?foo=bar'
静态文件(Static files)
为了确保 Sanic 可以正确代理静态文件,请使用 app.static()
方法进行路由分配。
在这里,参数的顺序十分重要
第一个参数是静态文件所需要匹配的路由
第二个参数是渲染文件所在的文件(夹)路径
app.static("/static", "/path/to/directory")
>> > app.url_for("get_handler", foo="bar")
'/get?foo=bar'
如果需要设置多个静态文件路由,我们需要手动为static()加上name参数,可以减少隐藏的Bug
app.static("/user/uploads", "/path/to/uploads", name="uploads")
app.static("/user/profile","/path/to/profile", name="profile_pics")
路由上下文
定义路由,可以添加任意数量的带有ctx_前缀的关键字参数,这些值会被注入到路由的ctx对象中
@app.get("/1",ctx_label="something")
async def handler1(request):
pass
@app.get("/2",ctx_label="something")
async def handler2(request):
pass
@app.on_request
async def do_something(request):
if request.route.ctx.label == "something":
pass
中间件(请求中间件,响应中间件)
启用
执行响应函数之前或者响应函数之后执行中间件,响应函数的构建,并将其挂载到 request
或 response
上
async def extract_user(request):
request.ctx.user = await extract_user_from_request(request):
app.register_middleware(extract_user, "request")
- 中间件使用装饰器挂载
app.MidderWare(requst)
async def test(request):
request.ctx.user = await extract_user_from_request(request)
- 响应中间件需要同时接收
request
和response
两个参数
@app.middleware('response')
async def prevent_xss(request, response):
response.headers["x-xss-protection"] = "1; mode=block"
- 进一步缩短该装饰器的调用代码
@app.on_request
async def extract_user(request):
...
@app.on_response
async def prevent_xss(request, response):
...
变更
您的中间件 不涉及返回响应操作,那么您可以使用中间件来修改请求参数或者响应参数。
@app.middleware("request")
async def add_key(request):
# Arbitrary data may be stored in request context:
request.ctx.foo = "bar"
@app.middleware("response")
async def custom_banner(request, response):
response.headers["Server"] = "Fake-Server"
@app.middleware("response")
async def prevent_xss(request, response):
response.headers["x-xss-protection"] = "1; mode=block"
@app.get("/")
async def index(request):
return text(request.ctx.foo)
您可以修改 request.match_info
。这个功能可能很有用,比如下面这个例子中,在中间件里将 a-slug
改变为 a_slug
。
@app.on_request
def convert_slug_to_underscore(request: Request):
request._match_info["slug"] = request._match_info["slug"].replace("-", "_")
@app.get("/<slug:[a-z0-9]+(?:-[a-z0-9]+)*>")
async def handler(request, slug):
return text(slug)
提前响应
如果中间件返回一个HTTPR额搜谱暖色对象, 那么请求将会终止,此对象将会作为最终响应进行返回,如果这个操作发生在响应函数之前,那么响应函数也不会被调用,除此之外,此操作同样不会调用该中间件之后的其他中间件。您可以返回 None
值来跳过某个中间件的执行,如果这样的话将不影响后续中间件的执行。您可以将这个特性用于在提前响应中中间件的选择性执行。
请求头
@app.route("/")
async def test(request):
return text(request.token)
request.token 可以获取身份令牌
request.host 属性来获取有效主机名
request.headers 可以获取真实的主机头信息
request.id 获取Id信息
有效的主机名称也可以与 request.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"),
}
)
您可以在请求对象的 request.headers
属性中获取所有的请求头,并且可以通过字典的方式来进行访问。Headers 的键名不考虑大小写,可以通过大写或小写键名来进行访问。
request.headers 对象是少数几个字典类型之一,每个值都是一个列表。这是因为HTTP允许重用一个键来发送多个值。
大多数情况下,您会希望使用 .get()或 .getone()方法访问第一个元素,而不是列表。如果您想要所有项目的列表,您可以使用 .getall() 方法。
响应头
content-length
content-type
connection
transfer-encoding
cookies
1.读取,通过request.cookies.get("...")
from sanic import Sanic
from sanic.response import text
@app.route("/cookie")
async def test(request)
test_cookie = request.cookies.get("test")
return text("test cookie:{}".format(test_cookie)
2.设置.通过Response对象的response.cookies来设置cookies
@app.route("/cookie")
async def test(requst):
response = text("there's a cookie up in this response")
response.cookies['test'] = "hahah"
return response
参数名称 | 参数类型 | 参数说明 |
---|---|---|
expires | datetime | Cookie 在客户端浏览器上失效的时间。 |
path | str | 此 Cookie 适用的 URL 子集。默认值为 / |
comment | str | 注释(元数据) |
domain | str | 指定 Cookie 的有效域。显式指定的域必须始终以点开始。 |
max-age | int | Cookie 应生存的秒数。 |
secure | bool | 指定是否仅通过 HTTPS 发送 Cookie。 |
httponly | bool | 指定 Javascript 是否无法读取 Cookie。 |
samesite | str | 默认值取决于浏览器,规范状态(Lax、Strict 和 None)是有效值。 |
3.删除,通过del
@app.route("/cookie")
async def test(request):
response = text("Time to eat some cookies muahaha")
# 此 cookie 将被立即删除
del response.cookies["kill_me"]
# 此 cookie 将在 5 秒后删除
response.cookies["short_life"] = "Glad to be here"
response.cookies["short_life"]["max-age"] = 5
del response.cookies["favorite_color"]
# 此 cookie 将保持不变
response.cookies["favorite_color"] = "blue"
response.cookies["favorite_color"] = "pink"
del response.cookies["favorite_color"]
return response
创建任务 add_task()
async def slow_work(...):
...
app = Sanic(...)
app.add_task(slow_work) # Note: we are passing the callable and not coroutine object `slow_work(...)`
app.run(...)
python3.8以上版本还支持给任务重命名,
app.add_task(slow_work, name="slow_task")
之后还可以可以使用 get_task
方法从应用程序的任何地方查看您的任务。
task = app.get_task("slow_task")
您想要取消任务,您可以通过 cacle_task
来进行操作,当然,该方法也是异步的,请确保使用时添加了 await
。
await app.cancel_task("slow_task")
所有注册的任务都可以在 app.tasks
属性中找到。为了防止已取消的任务填满,您可能需要运行 app.purge_tasks
来清除所有已完成或已取消的任务。
app.purge_tasks()