from fasthtml.common import *路由
FastHTML 应用中的行为由路由定义。其语法与出色的 FastAPI 大致相同(如果你在创建 JSON 服务,那么应该使用 FastAPI 而不是这个。FastHTML 主要用于制作 HTML Web 应用,而不是 API)。
我们尚未为 FastHTML 的所有路由功能编写完整的文档——在我们添加之前,了解所有可用功能的最佳方式是查阅测试用例。
请注意,你需要包含参数的类型,以便 FastHTML 知道要向你的函数传递什么。在这里,我们只期望一个字符串。
app = FastHTML()
@app.get('/user/{nm}')
def get_nm(nm:str): return f"Good day to you, {nm}!"通常你会将此代码保存到一个文件(例如 main.py)中,然后使用 uvicorn 来运行它:
uvicorn main:app
但是,为了测试,我们可以使用 Starlette 的 TestClient 来尝试一下。
from starlette.testclient import TestClientclient = TestClient(app)
r = client.get('/user/Jeremy')
r<Response [200 OK]>
TestClient 在底层使用 httpx,因此它返回一个 httpx.Response 对象,该对象有一个包含我们响应体的 text 属性。
r.text'Good day to you, Jeremy!'
在前面的例子中,函数名(get_nm)实际上并不重要——例如,我们本可以称之为 _,因为我们从未直接调用它。它只是通过 HTTP 被调用。事实上,在使用这种风格的路由时,我们经常将函数命名为 _,因为这样就少了一件需要担心的事情:命名。
创建路由的另一种方法是使用 app.route,在这种情况下,你需要将函数名设为你想要的 HTTP 方法。由于这是一种非常常见的模式,你可能会想给 app.route 起一个更短的名字——我们通常使用 rt。
rt = app.route
@rt('/')
def post(): return "Going postal!"
client.post('/').text'Going postal!'
特定于路由的功能
FastHTML 支持使用自定义装饰器为路由添加特定功能。这允许你为单个路由实现身份验证、授权、中间件或其他自定义行为。
这是一个基本身份验证装饰器的示例:
from functools import wraps
def basic_auth(f):
@wraps(f)
async def wrapper(req, *args, **kwargs):
token = req.headers.get("Authorization")
if token == 'abc123':
return await f(req, *args, **kwargs)
return Response('Not Authorized', status_code=401)
return wrapper
@app.get("/protected")
@basic_auth
async def protected(req):
return "Protected Content"
client.get('/protected', headers={'Authorization': 'abc123'}).text'Protected Content'
该装饰器在路由函数执行前拦截请求。如果装饰器允许请求继续,它会调用原始的路由函数,并将请求及任何其他参数传递给它。
这种方法的一个关键优势是能够对不同的路由应用不同的行为。你还可以在单个路由上堆叠多个装饰器以组合功能。
def app_beforeware():
print('App level beforeware')
app = FastHTML(before=Beforeware(app_beforeware))
client = TestClient(app)
def route_beforeware(f):
@wraps(f)
async def decorator(*args, **kwargs):
print('Route level beforeware')
return await f(*args, **kwargs)
return decorator
def second_route_beforeware(f):
@wraps(f)
async def decorator(*args, **kwargs):
print('Second route level beforeware')
return await f(*args, **kwargs)
return decorator
@app.get("/users")
@route_beforeware
@second_route_beforeware
async def users():
return "Users Page"
client.get('/users').textApp level beforeware
Route level beforeware
Second route level beforeware
'Users Page'
这种灵活性允许对路由行为进行精细控制,使你能够根据需要定制每个端点的功能。虽然应用级别的“前件”(beforeware)对于全局操作仍然有用,但装饰器为特定于路由的定制提供了强大的工具。
组合路由
有时一个 FastHTML 项目会变得非常庞大,以至于将所有路由都放在 main.py 中会变得难以管理。或者,我们安装了一个基于 FastHTML 或 Starlette 的包,需要我们添加它的路由。
首先,让我们创建一个 books.py 模块,用来表示所有与书籍相关的视图。
# books.py
books_app, rt = fast_app()
books = ['A Guide to FastHTML', 'FastHTML Cookbook', 'FastHTML in 24 Hours']
@rt("/", name="list")
def get():
return Titled("Books", *[P(book) for book in books])让我们在主模块中挂载它。
from books import books_app
1app, rt = fast_app(routes=[Mount("/books", books_app, name="books")])
@rt("/")
def get():
return Titled("Dashboard",
2 P(A(href="/books")("Books")),
Hr(),
3 P(A(link=uri("books:list"))("Books")),
)
serve()- 1
-
我们使用
starlette.Mount将路由添加到我们的路由列表中。我们提供名称books,以便于发现和管理链接。更多相关信息请参阅此注解列表的第 2 项和第 3 项。 - 2
- 这个指向书籍列表视图的示例链接是手动创建的。虽然目的明确,但这使得将来更改链接模式变得更加困难。
- 3
- 这个示例链接使用了为书籍路由命名的 URL。这种方法的优点是它使管理大量链接变得更加容易。