2023最新中级难度Fast API面试题,包含答案。刷题必备!记录一下。

发布时间 2023-12-28 12:23:00作者: 小满独家

好记性不如烂笔头

内容来自 [ 面试宝典-中级难度Fast API面试题合集](https://offer.houxu6.top/tag/Fast API)

问: FastAPI是一个基于HTTP协议的Python Web框架,请问FastAPI有哪些特点和优势?

FastAPI是一个现代、高性能的Python Web框架,用于构建RESTful API和Web服务。以下是FastAPI的一些主要特点和优势:

  1. 易学易用

    • FastAPI的设计易于学习和使用,特别是对于熟悉Python的人而言。
    • 语法简洁明了,且具有高度可读性。
  2. 高性能

    • FastAPI声称性能可以与Node.js和Go相媲美,是最快的Python框架之一。
    • 这得益于其基于Starlette和Pydantic库的实现。
  3. 快速编码

    • 开发速度比传统方法提高约200%到300%,这有助于缩短项目开发周期。
  4. 类型安全

    • 利用Python的类型提示来提供更强的代码检查和错误预防能力。
    • 自动文档生成:利用类型信息自动生成交互式的API文档(Swagger UI或ReDoc)。
  5. 安全性

    • 支持OpenAPI v3规范,并通过JSON Schema验证请求和响应数据。
    • 内置支持多种认证和授权方式,如OAuth2等。
  6. 异步处理

    • 基于异步IO(asyncio),使得FastAPI能够高效地处理高并发场景。
  7. 轻量级

    • 虽然功能强大,但FastAPI仍然是一个轻量级的框架,依赖较少。
  8. 兼容性

    • 兼容各大主流Web服务器,包括Uvicorn、Gunicorn等。
  9. 社区支持

    • FastAPI有一个活跃的开发者社区,因此有丰富的第三方库和支持资源。
  10. 可扩展性

    • 可以轻松地与其他库和框架集成,以满足特定项目需求。

这些特性使得FastAPI成为许多企业和开发者在构建高性能API时的理想选择。

问: FastAPI如何处理请求和响应的HTTP头部信息?

在FastAPI中,可以使用HTTPException和自定义响应类来处理请求和响应的HTTP头部信息。以下是一些方法:

  1. 通过 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)
  1. 使用 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模型来定义预期的数据结构即可。以下是一个简单的例子:

  1. 首先,创建一个Pydantic模型
    • 这个模型将定义你期望从客户端接收的数据结构。
from pydantic import BaseModel

class Item(BaseModel):
    name: str
    description: str = None
  1. 然后,在路由函数中使用 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]

在这个例子中,我们增加了两个额外的查询参数:skiplimit。这两个参数都是可选的,并且都有默认值。如果用户提供了这些参数(例如:/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的示例:

  1. 首先,你需要生成或获取一个SSL证书和私钥文件。你可以使用openssl命令行工具来生成它们,或者从权威证书颁发机构购买。

  2. 然后,在你的应用程序入口点(如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密钥文件和证书文件的路径。

  1. 最后,运行你的应用程序。你应该能够通过HTTPS访问它,例如:https://localhost:443/

请注意,如果你正在生产环境中部署你的应用程序,你可能还需要考虑其他安全措施,如HTTP严格传输安全(HSTS)、TLS版本控制等。

问: FastAPI如何处理Session管理?

在FastAPI中,处理Session管理可以使用第三方库fastapi-sessions。以下是一个示例:

  1. 首先,安装fastapi-sessions库:
pip install fastapi-sessions
  1. 然后,在你的应用程序入口点(如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。

  1. 最后,你可以在路由处理器中使用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)来执行这些操作。这样可以提高应用程序的性能和可扩展性。