2023最新中级难度Fast API面试题,包含答案。刷题必备!记录一下。
好记性不如烂笔头
内容来自 [ 面试宝典-中级难度Fast API面试题合集](https://offer.houxu6.top/tag/Fast API)
问: FastAPI是一个基于HTTP协议的Python Web框架,请问FastAPI有哪些特点和优势?
FastAPI是一个现代、高性能的Python Web框架,用于构建RESTful API和Web服务。以下是FastAPI的一些主要特点和优势:
-
易学易用:
- FastAPI的设计易于学习和使用,特别是对于熟悉Python的人而言。
- 语法简洁明了,且具有高度可读性。
-
高性能:
- FastAPI声称性能可以与Node.js和Go相媲美,是最快的Python框架之一。
- 这得益于其基于Starlette和Pydantic库的实现。
-
快速编码:
- 开发速度比传统方法提高约200%到300%,这有助于缩短项目开发周期。
-
类型安全:
- 利用Python的类型提示来提供更强的代码检查和错误预防能力。
- 自动文档生成:利用类型信息自动生成交互式的API文档(Swagger UI或ReDoc)。
-
安全性:
- 支持OpenAPI v3规范,并通过JSON Schema验证请求和响应数据。
- 内置支持多种认证和授权方式,如OAuth2等。
-
异步处理:
- 基于异步IO(asyncio),使得FastAPI能够高效地处理高并发场景。
-
轻量级:
- 虽然功能强大,但FastAPI仍然是一个轻量级的框架,依赖较少。
-
兼容性:
- 兼容各大主流Web服务器,包括Uvicorn、Gunicorn等。
-
社区支持:
- FastAPI有一个活跃的开发者社区,因此有丰富的第三方库和支持资源。
-
可扩展性:
- 可以轻松地与其他库和框架集成,以满足特定项目需求。
这些特性使得FastAPI成为许多企业和开发者在构建高性能API时的理想选择。
问: FastAPI如何处理请求和响应的HTTP头部信息?
在FastAPI中,可以使用HTTPException
和自定义响应类来处理请求和响应的HTTP头部信息。以下是一些方法:
- 通过
HTTPException
设置状态码和头部:- 当需要返回错误或异常响应时,可以使用
HTTPException
类。 - 你可以指定一个可选的
headers
参数来添加HTTP头部。
- 当需要返回错误或异常响应时,可以使用
from fastapi import FastAPI, HTTPException
app = FastAPI()
@app.get("/")
async def main():
headers = {"X-Custom-Header": "Value"}
raise HTTPException(status_code=403, detail="Access denied", headers=headers)
- 使用
Response
或自定义响应类设置头部:- 在某些情况下,你可能希望控制整个响应对象,包括其状态码、内容和头部。
- 使用
fastapi.responses.Response
类或创建自己的自定义响应类。
from fastapi import FastAPI, Response
from fastapi.responses import JSONResponse
app = FastAPI()
@app.get("/")
async def main():
headers = {"X-Custom-Header": "Value"}
content = {"message": "Hello, world!"}
return Response(content=json.dumps(content), media_type="application/json", headers=headers)
# 或者使用JSONResponse
from pydantic import BaseModel
class CustomResponse(BaseModel):
message: str
@app.get("/")
async def main():
headers = {"X-Custom-Header": "Value"}
response = CustomResponse(message="Hello, world!")
return JSONResponse(content=response.dict(), status_code=200, headers=headers)
以上代码展示了如何在FastAPI中处理请求和响应的HTTP头部信息。根据你的需求,可以选择最适合的方法来实现所需的头部功能。
问: FastAPI如何处理POST请求的JSON数据?
在FastAPI中,处理POST请求的JSON数据非常简单。只需使用Body
类和Pydantic模型来定义预期的数据结构即可。以下是一个简单的例子:
- 首先,创建一个Pydantic模型:
- 这个模型将定义你期望从客户端接收的数据结构。
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
- 然后,在路由函数中使用
Body
类:- 在路由装饰器中,将参数类型设置为
Body
类,并传递你的Pydantic模型作为参数。
- 在路由装饰器中,将参数类型设置为
from fastapi import FastAPI, Body
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item = Body(...)):
return item
在这个例子中,FastAPI会自动解析并验证接收到的JSON数据,将其转换为 Item
对象,并传递给路由函数。如果请求中的JSON数据不符合 Item
模型的要求(例如缺少必要的字段或包含无效的值),FastAPI将返回一个错误响应。
注意:这里的 Body(...)
是一个特殊语法,表示此参数是必需的。如果你想让某个参数可选,可以使用 Body(None)
或 Body(x=None)
,其中 x
是默认值。
此外,FastAPI还支持其他方法来处理请求体,包括表单数据、多部分文件等。你可以查阅FastAPI官方文档以获取更多信息。
问: FastAPI如何处理GET请求的查询参数?
在FastAPI中,处理GET请求的查询参数非常简单。首先,你需要定义一个函数来处理这些参数。然后,在这个函数上使用@app.get装饰器,并将需要的查询参数添加到路径操作装饰器中。
以下是一个示例:
from fastapi import FastAPI
app = FastAPI()
@app.get("/items/")
async def read_items(q: str):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results["q"] = q
return results
在这个例子中,我们有一个名为read_items
的函数,它接受一个名为q
的查询参数。当用户访问/items/
路径并提供查询参数(例如:/items?q=foo
)时,FastAPI会自动将该查询参数解析为字符串,并将其作为参数传递给read_items
函数。
如果你需要接收多个查询参数,你可以像下面这样定义你的函数:
@app.get("/items/")
async def read_items(q: str, skip: int = 0, limit: int = 10):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results["q"] = q
return results[skip:limit]
在这个例子中,我们增加了两个额外的查询参数:skip
和limit
。这两个参数都是可选的,并且都有默认值。如果用户提供了这些参数(例如:/items?q=foo&skip=10&limit=5
),FastAPI会将它们解析为整数,并将它们作为参数传递给read_items
函数。
问: FastAPI如何处理文件上传请求?
在FastAPI中,处理文件上传请求非常简单。首先,你需要定义一个函数来处理这些上传的文件。然后,在这个函数上使用@app.post装饰器,并将需要的文件参数添加到路径操作装饰器中。
以下是一个示例:
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/uploadfile/")
async def create_upload_file(file: bytes = File(...)):
return {"file_size": len(file)}
在这个例子中,我们有一个名为create_upload_file
的函数,它接受一个名为file
的文件参数。当用户访问/uploadfile/
路径并上传文件时,FastAPI会自动将该文件解析为字节流,并将其作为参数传递给create_upload_file
函数。
如果你想要接收多个文件,你可以像下面这样定义你的函数:
from typing import List
from fastapi import File, UploadFile
@app.post("/uploadfiles/")
async def create_upload_files(files: List[bytes] = File(...)):
return {"file_sizes": [len(file) for file in files]}
在这个例子中,我们增加了多个文件参数:files
。如果用户提供了这些参数(例如:同时上传多个文件),FastAPI会将它们解析为字节流列表,并将它们作为参数传递给create_upload_files
函数。
另外,如果你想获取上传文件的原始名称和MIME类型等信息,可以使用UploadFile
对象代替bytes
类型。以下是使用UploadFile
的例子:
from fastapi import FastAPI, File, UploadFile
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
content = await file.read()
return {"filename": file.filename, "content_type": file.content_type, "file_size": len(content)}
在这个例子中,我们可以从UploadFile
对象中获取上传文件的原始名称、MIME类型以及内容。
问: FastAPI如何处理WebSocket连接?
在FastAPI中,处理WebSocket连接可以使用Starlette的WebSocket功能。首先,你需要定义一个函数来处理WebSocket连接和数据传输。然后,在这个函数上使用@app.websocket装饰器,并将需要的路径添加到装饰器中。
以下是一个示例:
from fastapi import FastAPI
from fastapi.responses import HTMLResponse
from starlette.websockets import WebSocket
app = FastAPI()
html = """
<!DOCTYPE html>
<html>
<head>
<title>WebSocket Echo</title>
</head>
<body>
<h1>WebSocket Echo</h1>
<form action="" onsubmit="sendMessage(event)">
<input type="text" id="messageText" autocomplete="off"/>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = new WebSocket("ws://localhost:8000/ws/echo");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message);
};
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value);
event.preventDefault()
}
</script>
</body>
</html>
"""
@app.get("/")
async def get():
return HTMLResponse(html)
@app.websocket("/ws/echo/")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"You said: {data}")
在这个例子中,我们有一个名为websocket_endpoint
的函数,它接受一个名为websocket
的WebSocket对象。当用户通过WebSocket连接访问/ws/echo/
路径时,FastAPI会自动创建一个新的WebSocket连接,并将其作为参数传递给websocket_endpoint
函数。
在websocket_endpoint
函数内部,我们首先调用await websocket.accept()
来接受新的WebSocket连接。然后,我们进入一个无限循环,等待接收客户端发送的数据(通过await websocket.receive_text()
)。每当收到新数据时,我们就将数据回显给客户端(通过await websocket.send_text()
)。
请注意,为了使这个示例工作,你需要提供一个HTML页面,其中包含一个与WebSocket服务器建立连接的JavaScript代码。你可以参考上面示例中的HTML代码来实现这一点。
问: FastAPI如何处理Cookie?
在FastAPI中,处理Cookie非常简单。你可以使用fastapi.responses.Response
对象的set_cookie
方法来设置Cookie,或者使用fastapi.requests.Request
对象的cookies
属性来获取Cookie。
以下是一个示例:
from fastapi import FastAPI, Request, Response
app = FastAPI()
@app.get("/set-cookie/")
async def set_cookie(response: Response):
response.set_cookie(key="cookie_name", value="cookie_value")
return {"message": "Cookie has been set"}
@app.get("/get-cookie/")
async def get_cookie(request: Request):
cookie_value = request.cookies.get("cookie_name")
if cookie_value:
return {"message": f"Cookie value is {cookie_value}"}
else:
return {"message": "Cookie not found"}
在这个例子中,我们有两个路由:一个用于设置Cookie(/set-cookie/
),另一个用于获取Cookie(/get-cookie/
)。
在set_cookie
函数中,我们首先创建了一个Response
对象,并调用了它的set_cookie
方法来设置Cookie。这个方法接受两个参数:一个是Cookie的名称,另一个是Cookie的值。然后,我们返回一个包含消息的字典。
在get_cookie
函数中,我们首先从Request
对象中获取了所有可用的Cookie,然后通过键(即Cookie的名称)查找特定的Cookie。如果找到了该Cookie,我们就返回一个包含消息和Cookie值的字典;否则,我们返回一个表示找不到Cookie的消息。
请注意,你还可以为set_cookie
方法提供其他选项,如过期时间、路径、域等。例如:
response.set_cookie(
key="cookie_name",
value="cookie_value",
max_age=3600,
path="/",
domain="example.com",
)
这将设置一个名为“cookie_name”的Cookie,其值为“cookie_value”,有效期为一小时,路径为"/",域为“example.com”。
问: FastAPI如何处理HTTPS请求?
在FastAPI中,处理HTTPS请求需要配置服务器以支持HTTPS。以下是在Uvicorn(一个常用的ASGI服务器)上启用HTTPS的示例:
-
首先,你需要生成或获取一个SSL证书和私钥文件。你可以使用
openssl
命令行工具来生成它们,或者从权威证书颁发机构购买。 -
然后,在你的应用程序入口点(如
main.py
)中添加以下代码,以指定证书和私钥文件的位置:
from fastapi import FastAPI
import uvicorn
app = FastAPI()
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=443, ssl_keyfile="path/to/your/key.pem", ssl_certfile="path/to/your/cert.pem")
在这个例子中,我们调用了uvicorn.run
函数,并传入了四个参数:应用程序对象、主机地址、端口号以及SSL密钥文件和证书文件的路径。
- 最后,运行你的应用程序。你应该能够通过HTTPS访问它,例如:https://localhost:443/
请注意,如果你正在生产环境中部署你的应用程序,你可能还需要考虑其他安全措施,如HTTP严格传输安全(HSTS)、TLS版本控制等。
问: FastAPI如何处理Session管理?
在FastAPI中,处理Session管理可以使用第三方库fastapi-sessions
。以下是一个示例:
- 首先,安装
fastapi-sessions
库:
pip install fastapi-sessions
- 然后,在你的应用程序入口点(如
main.py
)中添加以下代码,以设置Session的存储和加密方式:
from fastapi import FastAPI, Request
from fastapi_sessions import SessionManager, CookieBackend, EncryptedCookieSerializer
app = FastAPI()
session_manager = SessionManager(
backend=CookieBackend(serializer=EncryptedCookieSerializer(secret_key="your_secret_key")),
cookie_name="session_id",
)
async def get_session(request: Request):
return await session_manager.get_session(request=request)
@app.on_event("startup")
async def startup():
await session_manager.startup()
@app.on_event("shutdown")
async def shutdown():
await session_manager.shutdown()
在这个例子中,我们创建了一个SessionManager
对象,并指定了一个基于Cookie的后端以及一个用于加密Cookie的序列化器。我们还定义了一个异步函数get_session
,它从请求对象中获取当前的Session。
- 最后,你可以在路由处理器中使用
get_session
函数来操作Session。例如:
@app.post("/login/")
async def login(username: str, password: str, request: Request):
user = authenticate_user(username, password)
if user is None:
return {"message": "Invalid username or password"}
session = await get_session(request)
session["user_id"] = user.id
await session_manager.commit(session=session)
return {"message": "Login successful"}
在这个例子中,我们在用户登录时将用户的ID保存到Session中,并在后续请求中通过Session来识别已登录的用户。
问: FastAPI如何处理异步请求?
在FastAPI中,处理异步请求非常简单。只需将你的函数定义为异步函数(使用async def
),并使用关键字await
来调用其他异步函数或操作即可。
以下是一个示例:
from fastapi import FastAPI, HTTPException
import requests
app = FastAPI()
@app.get("/items/{item_id}")
async def read_item(item_id: int):
response = await fetch_item_data(item_id)
if not response:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": response}
async def fetch_item_data(item_id: int):
url = f"https://example.com/items/{item_id}"
response = requests.get(url) # 这里实际上是同步的,但你可以换成一个异步库(如httpx)
if response.status_code == 200:
return response.json()
else:
return None
在这个例子中,我们有一个名为read_item
的路由处理器,它接受一个参数item_id
。然后,我们调用了fetch_item_data
异步函数来获取该物品的数据。如果找不到该物品,我们就抛出一个HTTP异常;否则,我们返回包含物品数据的响应。
请注意,虽然这个例子中的fetch_item_data
函数实际上执行了一个同步操作(通过requests库发送HTTP请求),但在实际应用中,你通常会使用异步库(如httpx)来执行这些操作。这样可以提高应用程序的性能和可扩展性。