HTTP 路由
Starlette 拥有一个简单但功能强大的请求路由系统。路由表被定义为路由列表,并在实例化应用程序时传递。
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route
async def homepage(request):
return PlainTextResponse("Homepage")
async def about(request):
return PlainTextResponse("About")
routes = [
Route("/", endpoint=homepage),
Route("/about", endpoint=about),
]
app = Starlette(routes=routes)
endpoint
参数可以是以下之一:
- 一个常规函数或异步函数,它接受一个
request
单一参数,并且应该返回一个响应。 - 一个实现 ASGI 接口的类,例如 Starlette 的 HTTPEndpoint 。
路径参数
路径可以使用 URI 模板样式来捕获路径组件。
Route('/users/{username}', user)
默认情况下,这将捕获字符,直至路径末尾或下一个 /
。
您可以使用转换器来修改所捕获的内容。可用的转换器为:
str
返回一个字符串,并且是默认值。int
返回一个 Python 整数。float
返回一个 Python 浮点数。uuid
返回一个 Pythonuuid.UUID
实例。path
返回路径的其余部分,包括任何额外的/
字符。
转换器通过在其前加上冒号来使用,如下所示:
Route('/users/{user_id:int}', user)
Route('/floating-point/{number:float}', floating_point)
Route('/uploaded/{rest_of_path:path}', uploaded)
如果您需要一个未定义的不同转换器,您可以创建自己的。请参阅下面有关如何创建 datetime
转换器以及如何注册它的示例:
from datetime import datetime
from starlette.convertors import Convertor, register_url_convertor
class DateTimeConvertor(Convertor):
regex = "[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}(.[0-9]+)?"
def convert(self, value: str) -> datetime:
return datetime.strptime(value, "%Y-%m-%dT%H:%M:%S")
def to_string(self, value: datetime) -> str:
return value.strftime("%Y-%m-%dT%H:%M:%S")
register_url_convertor("datetime", DateTimeConvertor())
注册后,您将能够将其用作:
Route('/history/{date:datetime}', history)
路径参数在请求中可用,作为 request.path_params
字典。
async def user(request):
user_id = request.path_params['user_id']
...
处理 HTTP 方法
路由还可以指定端点处理哪些 HTTP 方法:
Route('/users/{user_id:int}', user, methods=["GET", "POST"])
默认情况下,函数端点将仅接受 GET
请求,除非另有指定。
子底座安装布线
在大型应用程序中,您可能会发现您想要根据常见的路径前缀拆分路由表的部分内容。
routes = [
Route('/', homepage),
Mount('/users', routes=[
Route('/', users, methods=['GET', 'POST']),
Route('/{username}', user),
])
]
这种风格允许您在项目的不同部分定义路由表的不同子集。
from myproject import users, auth
routes = [
Route('/', homepage),
Mount('/users', routes=users.routes),
Mount('/auth', routes=auth.routes),
]
您还可以使用挂载将子应用程序包含在您的 Starlette 应用程序中。例如……
# This is a standalone static files server:
app = StaticFiles(directory="static")
# This is a static files server mounted within a Starlette application,
# underneath the "/static" path.
routes = [
...
Mount("/static", app=StaticFiles(directory="static"), name="static")
]
app = Starlette(routes=routes)
反向 URL 查找
您通常会希望能够为特定路由生成 URL,例如在需要返回重定向响应的情况下。
-
签名:
url_for(name, **path_params) -> URL
routes = [ Route("/", 主页页面, name="homepage") ]
我们可以使用以下内容来返回一个 URL……
url = request.url_for("首页")
URL 查找可以包含路径参数……
routes = [
Route("/users/{username}", user, name="user_detail")
]
# We can use the following to return a URL...
url = request.url_for("user_detail", username=...)
如果一个 Mount
包含一个 name
,那么底座应该使用 {prefix}:{name}
样式进行反向 URL 查找。
routes = [
Mount("/users", name="users", routes=[
Route("/", user, name="user_list"),
Route("/{username}", user, name="user_detail")
])
]
# We can use the following to return URLs...
url = request.url_for("users:user_list")
url = request.url_for("users:user_detail", username=...)
安装的应用程序可能包含一个 path=...
参数。
routes = [
...
Mount("/static", app=StaticFiles(directory="static"), name="static")
]
# We can use the following to return URLs...
url = request.url_for("static", path="/css/base.css")
对于没有 request
实例的情况,您可以针对应用程序进行反向查找,尽管这些查找只会返回 URL 路径。
url = app.url_path_for("user_detail", username=...)
基于主机的路由
如果您想根据 Host
标头为相同路径使用不同的路由。
请注意,在匹配时会从 Host
标头中删除端口。例如,即使 Host
标头包含或不包含除 3600
( example.org:5600
, example.org
)之外的端口, Host (host='example.org:3600', ...)
也会被处理。因此,如果您在 url_for
中需要使用端口,则可以指定该端口。
有几种方法可将基于主机的路由连接到您的应用程序
site = Router() # Use eg. `@site.route()` to configure this.
api = Router() # Use eg. `@api.route()` to configure this.
news = Router() # Use eg. `@news.route()` to configure this.
routes = [
Host('api.example.org', api, name="site_api")
]
app = Starlette(routes=routes)
app.host('www.example.org', site, name="main_site")
news_host = Host('news.example.org', news)
app.router.routes.append(news_host)
URL 查询可以包含主机参数,就像路径参数一样
routes = [
Host("{subdomain}.example.org", name="sub", app=Router(routes=[
Mount("/users", name="users", routes=[
Route("/", user, name="user_list"),
Route("/{username}", user, name="user_detail")
])
]))
]
...
url = request.url_for("sub:users:user_detail", username=..., subdomain=...)
url = request.url_for("sub:users:user_list", subdomain=...)
路由优先级
传入路径按顺序与每个 Route
进行匹配。
在可能有多个路由与传入路径匹配的情况下,您应注意确保在一般情况之前列出更具体的路由。
例如:
# Don't do this: `/users/me` will never match incoming requests.
routes = [
Route('/users/{username}', user),
Route('/users/me', current_user),
]
# Do this: `/users/me` is tested first.
routes = [
Route('/users/me', current_user),
Route('/users/{username}', user),
]
使用路由器实例进行工作
如果您在低级别工作,您可能想要使用普通的 Router
实例,而不是创建 Starlette
应用程序。这为您提供了一个轻量级的 ASGI 应用程序,该应用程序仅提供应用程序路由,而不会将其包装在任何中间件中。
app = Router(routes=[
Route('/', homepage),
Mount('/users', routes=[
Route('/', users, methods=['GET', 'POST']),
Route('/{username}', user),
])
])
WebSocket 路由
在使用 WebSocket 端点时,您应该使用 WebSocketRoute
而不是通常的 Route
。
路径参数以及针对 WebSocketRoute
的反向 URL 查找的工作方式与 HTTP Route
相同,这在上面的 HTTP 路由部分可以找到。
from starlette.applications import Starlette
from starlette.routing import WebSocketRoute
async def websocket_index(websocket):
await websocket.accept()
await websocket.send_text("Hello, websocket!")
await websocket.close()
async def websocket_user(websocket):
name = websocket.path_params["name"]
await websocket.accept()
await websocket.send_text(f"Hello, {name}")
await websocket.close()
routes = [
WebSocketRoute("/", endpoint=websocket_index),
WebSocketRoute("/{name}", endpoint=websocket_user),
]
app = Starlette(routes=routes)
endpoint
参数可以是以下之一:
- 一个异步函数,它接受一个单一的
websocket
参数。 - 一个实现 ASGI 接口的类,例如 Starlette 的 WebSocketEndpoint 。