node部分
1.node的koa和express的中间件模型对比?
Express:线性中间件(瀑布模型),而koa是洋葱模型
express(瀑布模型):
- 普通中间件:
(req,res,next)=>{} - 错误中间件:四个参数
(err, req, res, next)=>{} - 调用
next()才进入下一个中间件;next 之后代码不会等后面中间件执行完 ,直接继续执行当前中间件剩下代码
简单理解:顺序往下走,走完路由再一层层返回,不是洋葱。
koa(洋葱模型):
ctx:上下文,封装了 request 和 response,替代 express 的 req、resawait next()是洋葱的关键;await 会暂停当前中间件,去执行下游,下游全部跑完,再回来执行 await 后面代码
bash
// Koa中间件格式
async (ctx, next) => {
console.log('前置')
await next() // 等待下游所有中间件全部执行完毕,才回来执行下面代码
console.log('后置')
}
比如app.use(async (ctx, next) => {
console.log('First middleware (request phase)');
await next();
console.log('First middleware (response phase)');
});
app.use(async (ctx, next) => {
console.log('Second middleware (request phase)');
await next();
console.log('Second middleware (response phase)');
});
app.use(async (ctx) => {
console.log('Third middleware (final response)');
ctx.body = 'Hello from Koa!';
});
打印出来是:
First middleware (request phase)
Second middleware (request phase)
Third middleware (final response)
Second middleware (response phase)
First middleware (response phase)
在上面的例子中,await next() 被调用后,程序会继续执行下一个中间件,并在后续中间件执行完成后返回。
一句话区分:
Express next () 是调用下一个中间件,不等待返回 ;
Koa await next() 等待下游全部执行完成,再回来执行后置逻辑
面试坑:Koa 中间件执行顺序口诀:先进后出,像剥洋葱
2. ctx 对象是什么?
ctx = context,请求上下文,每一次请求都会新建一个 ctx
ctx.request:封装原生 reqctx.response:封装原生 res- 简写:
ctx.url、ctx.method、ctx.body(最常用,设置响应体)、ctx.status状态码 ctx.queryget 参数;ctx.params需要 koa-router 支持
3. Koa 为什么不内置路由、静态资源?
Koa 设计理念:内核极简,只提供中间件洋葱模型,其他能力交给第三方生态:
- 路由:
koa-router - 静态资源:
koa-static - body 解析:
koa-bodyparser
4. Koa 的错误处理(高频)
1.全局错误中间件:放在所有中间件最前面(洋葱模型顺序!)
bash
app.use(async (ctx, next) => {
try {
await next()
} catch (err) {
ctx.status = err.status || 500
ctx.body = { code: 500, msg: err.message }
}
})
2.主动抛出:ctx.throw(401, '未授权'),自动抛出错误对象
5. koa-bodyparser 作用
解析 post 请求 body,支持 json、表单;不能解析 multipart/form-data 上传文件 ,文件上传要用 koa-multer
python部分
一、FastAPI 基础概念
1. FastAPI 是什么?核心优势
- 基于 Starlette(web 底层)+ Pydantic(数据校验)的现代 Python Web 框架;
- 主打:自动 OpenAPI 文档、类型提示、自动数据校验、异步支持、高性能;
- 对比 Flask/Django:
- Flask:轻量,无内置校验,文档要手写,异步支持弱;
- Django:大而全,重型 ORM,适合传统后台;
- FastAPI:原生类型注解,自动校验 + 接口文档,同步 /async 混用,性能接近 Node/Go。
追问:Starlette 作用?
Starlette 是 ASGI web 框架,负责网络层:请求接收、路由、中间件、响应、websocket;FastAPI 在它之上封装,增加 Pydantic、依赖注入、openapi 生成。
2. ASGI 和 WSGI 的区别(必问)
- WSGI:同步网关,只能处理同步代码,Django/Flask;一个请求占一个 worker;
- ASGI:异步网关,支持
async/await,支持长连接(websocket、SSE);可以同时处理多个 IO 密集请求; - FastAPI 运行服务器:uvicorn、hypercorn;uvicorn 是 ASGI 服务器。
坑点:路由函数写普通 def,FastAPI 也能跑,uvicorn 会在线程池执行;只有 async def 才是原生异步。
Pydantic(FastAPI 灵魂,面试最高频)
1. Pydantic BaseModel 作用
- 数据模型定义、请求入参校验、类型转换、序列化;
- FastAPI 自动把请求 json → BaseModel 实例;返回模型自动转 json;
- 校验失败自动返回 422,附带详细错误信息。
2. Pydantic v1 vs v2(非常高频!现在项目大多迁移 v2)
- v1:底层 Python 实现;
- v2:底层 Rust 重写,速度提升几十倍;API 有少量 breaking change;
- 改动点:
@validator→@field_validator/@model_validatororm_mode = True→model_config = ConfigDict(from_attributes=True).dict()→.model_dump();.json()→.model_dump_json()
面试必背:
from_attributes=True用来支持从 ORM 对象(sqlalchemy)直接转 Pydantic 模型。
3. 字段校验:常用约束
Field(gt=0, max_length=100, description="xxx")
- 自定义校验:
@field_validator单字段校验;@model_validator(mode="before"/"after")多字段联合校验。
4. 模型嵌套、可选字段、默认值、Optional / | None
from typing import Optional
name: Optional[str] = None # python3.9+ 等价 str | None
面试坑:
name: str = None会报错!类型声明 str 不能是 None,必须显式标记可空。
5. 序列化 exclude、include
model_dump(exclude={"password"}) 返回时剔除敏感字段,接口返回脱敏常用。
三、请求、参数类型(高频)
FastAPI 自动区分 4 种参数:
- 路径参数 Path :
/items/{item_id},item_id - 查询参数 Query :
/items?limit=10,函数普通参数,不在路径里 - 请求体 Body :BaseModel,POST/PUT json;GET 不能带 body(http 规范)
- 表单参数 Form :
Form(),multipart/form-data,上传文件、表单提交
区分口诀:
在路径 {} 里 → Path;
不在路径,且是基础类型 (int/str) → Query;
是 Pydantic 模型 → Body;
File/Form → 表单。
特殊参数
Path(..., ge=1):路径参数校验Query(default=None, alias="page-size"):查询参数别名,前端用横杠,后端变量下划线File()上传文件,UploadFile对象:.filename、.read()、.size
四、依赖注入 Depends(FastAPI 最有特色的考点,必问)
1. Depends 是什么?
依赖注入系统,把公共逻辑抽成依赖函数,路由函数声明依赖,框架自动执行并注入返回值。
典型场景:
-
获取当前登录用户
-
获取数据库 session
-
权限校验
-
请求日志、限流
from fastapi import Depends
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()@app.get("/")
def hello(db = Depends(get_db)):
...
重点:yield 依赖(上下文依赖)
yield 之前:请求前执行;yield 返回值注入路由;yield 之后:请求结束后执行(关闭连接、释放资源)。
支持 async yield,同步 yield。
2. 依赖的作用域(面试高频)
- 默认:每个请求一次。每个请求进来,新建依赖实例,请求结束销毁。
- 没有全局单例(除非你自己用全局变量,不推荐,多线程 / 异步不安全)
- 依赖可以嵌套:Depends 里面还可以 Depends。
3. 依赖里面抛异常
在依赖函数里直接 raise HTTPException(401),路由不会执行,直接返回错误,适合鉴权。
五、async /await 异步(重点,结合你的 Agent 项目)
1. 同步函数 def vs async def 路由函数
@app.get("/sync")
def sync_func():
# 同步代码,uvicorn放到线程池运行
pass
@app.get("/async")
async def async_func():
# ASGI原生异步,不要在这里写time.sleep()!要用await asyncio.sleep()
pass
经典面试题:async 路由里面调用 time.sleep (10) 会发生什么?
✅ 答案:会阻塞整个事件循环!所有其他请求卡住。
异步代码中不能调用阻塞 IO(time.sleep、同步数据库、requests),要用异步库:httpx.AsyncClient、asyncpg。
同步 def 路由会丢到线程池,不会阻塞主事件循环,但线程池有上限。
2. 什么时候用 async?
IO 密集场景:数据库查询、http 调用(调用 LLM、RAG 向量库)、redis。
CPU 密集:不适合 async,放到任务队列(celery)。
3. 事件循环
ASGI 服务器启动一个主事件循环,async 代码都跑在这个循环里。
六、响应、状态码、响应模型
response_model=UserRes:过滤输出字段,自动序列化,屏蔽敏感字段。
注意:response_model 是对返回结果做校验 + 序列化,不是入参。
status_code=201:默认 200,创建资源用 201RedirectResponse、FileResponse、StreamingResponse
StreamingResponse(SSE 流式输出,你的 RAG/Agent 项目核心!大概率会问)
用于大模型流式打字效果,逐块返回文本,不会一次性加载全部内容。
要点:
- 传入生成器函数(普通生成器 /async 生成器)
- MediaType
text/event-stream- 适合 RAG 对话,SSE 长连接;
- 注意:如果用同步生成器,会占用线程池;优先 async 生成器。
七、异常处理(高频)
-
HTTPException(status_code, detail="msg"):基础抛出,返回 http 错误。 -
全局异常处理器
@app.exception_handler(自定义异常类)from fastapi import Request
@app.exception_handler(ValueError)
async def value_error_handler(request: Request, exc: ValueError):
return JSONResponse(status_code=400, content={"msg": str(exc)}) -
422 错误:Pydantic 校验失败,FastAPI 内置处理器。
-
RequestValidationError:请求参数校验异常。
面试题:HTTPException 和 自定义异常区别?
HTTPException 是 FastAPI 内置,简单场景;自定义异常 + 全局 handler 统一返回业务错误码结构(code、msg、data),项目常用。
八、中间件 Middleware
FastAPI 中间件本质是 Starlette 中间件。两种写法:
-
@app.middleware("http")装饰器写法(async)@app.middleware("http")
async def log_middleware(request: Request, call_next):
# 请求前
response = await call_next(request)
# 请求后
return response -
继承
BaseHTTPMiddleware类写法。
用途:全局日志、耗时统计、跨域 CORS、请求 ID 注入。
CORS:
from fastapi.middleware.cors import CORSMiddleware,允许跨域前端访问。
注意:中间件在依赖注入之前执行;中间件捕获异常需要自己处理。
九、安全模块 OAuth2、JWT(后端必问)
from fastapi.security import OAuth2PasswordBearer
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")
- OAuth2PasswordBearer:声明从 header Authorization: Bearer {token} 取 token。
- JWT 流程:登录成功,服务端生成 jwt 返回;后续请求携带 token;依赖里解析 token 拿到用户信息。
- JWT 组成:header.payload.signature;payload 不能存敏感信息(可解码)。
- 密码不要明文存储:passlib + bcrypt 哈希。
十、数据库相关(你项目里 PostgreSQL)
面试官喜欢问:同步 session vs async session
- SQLAlchemy v1/v2:同步 Session /async AsyncSession
- async 模式下:engine = create_async_engine (),session = AsyncSession
- 依赖 yield 方式管理 session:每次请求创建 session,请求结束自动 close。
坑:async session 里不能用同步 ORM 操作,必须 await。
十一、部署、性能、坑点
- uvicorn 启动命令:
uvicorn main:app --workers 4 --host 0.0.0.0 --port 8000
workers:多进程;多进程下,全局变量不共享,每个进程独立一份 ;异步单进程单事件循环,多进程利用多核。
生产一般:uvicorn + nginx 反向代理。
- 性能调优:
- Pydantic v2 升级;
- 数据库连接池;
- 慢 IO 使用异步客户端;
- 避免阻塞代码;
- 缓存 redis;
- 大响应用 StreamingResponse。
- 常见坑
- async 路由中调用同步阻塞库(requests/time.sleep)阻塞事件循环;
- 忘记关闭数据库连接,连接池耗尽;
- GET 请求传 body,http 规范不推荐,部分网关会丢弃 body;
- 依赖中全局变量做状态,多进程 / 并发下数据错乱;
- 忘记 CORS 导致前端跨域报错;
- Pydantic v1 升级 v2 语法踩坑。