Web 后端技术实战:Flask + AJAX/REST API + FastAPI 从零到上线
实战环境 : 华为云 FlexusX ECS | Ubuntu 24.04.4 | Python 3.12.3 | PostgreSQL 16.14 服务器 : ecs-88e7-0001 (139.9.128.210) | 8vCPU 16GiB | 可用区7 依赖版本 : Flask 3.1.3 | psycopg 3.3.4 | FastAPI 0.138.2 | Uvicorn
目录
一、Flask 基础篇
1.1 最小应用
from flask import Flask
app = Flask( __name__)
@app. route ( "/" )
def hello ( ) :
return "<h1>Hello, Flask!</h1>"
if __name__ == "__main__" :
app. run( host= "0.0.0.0" , port= 5000 , debug= True )
关键参数 :
host="0.0.0.0" — 允许外网访问(默认 127.0.0.1 仅本机)port=5000 — Flask 默认端口debug=True — 热重载 + 详细错误页(生产环境务必关闭)
GET / → 200 | 响应长度: 17
GET /api/status → 200 | {"status":"ok","framework":"Flask"}
1.2 调试模式与错误日志
import logging
handler = logging. FileHandler( "/root/flask_error.log" )
handler. setLevel( logging. ERROR)
app. logger. addHandler( handler)
@app. route ( "/error" )
def trigger_error ( ) :
app. logger. error( "捕获异常" , exc_info= True )
return "Error logged" , 500
GET /error → 500 | Error logged: division by zero
错误日志大小: 287 bytes
1.3 路由系统
动态路由(URL 变量)
@app. route ( "/user/<username>" )
def profile ( username) : . . .
@app. route ( "/post/<int:post_id>" )
def show_post ( post_id) : . . .
@app. route ( "/path/<path:subpath>" )
def catch_all ( subpath) : . . .
类型转换器 :
转换器 示例 匹配 string(默认)/user/<username>不含 / 的任意文本 int/post/<int:id>正整数 float/price/<float:amount>浮点数 path/files/<path:p>含 / 的路径 uuid/item/<uuid:uid>UUID 字符串
GET /user/alice → 200 | 用户: alice
GET /post/42 → 200 | 文章 #42
GET /path/a/b/c → 200 | 捕获路径: a/b/c
1.4 蓝图 (Blueprint) — 模块化
┌──────────────────────────────────────────┐
│ Flask 主应用 │
│ │
│ app.register_blueprint(auth_bp) │
│ app.register_blueprint(blog_bp) │
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ auth 蓝图 │ │ blog 蓝图 │ │
│ │ /auth/login │ │ /blog/ │ │
│ │ /auth/register│ │ /blog/<id> │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────┘
auth_bp = Blueprint( "auth" , __name__, url_prefix= "/auth" )
@auth_bp. route ( "/login" )
def login ( ) : return "登录页面"
app. register_blueprint( auth_bp)
GET /auth/login → 200 | 登录页面
GET /auth/register → 200 | 注册页面
GET /blog/ → 200 | 博客首页
GET /blog/99 → 200 | 博客文章 #99
1.5 模板渲染(Jinja2)
┌──────────────────────────────────────┐
│ Jinja2 模板系统 │
│ │
│ {{ variable }} 变量输出 │
│ {% if/for %} 控制流 │
│ {% block %} 块占位 │
│ {% extends %} 模板继承 │
│ {% include %} 模板包含 │
│ {{ var|filter }} 过滤器 │
└──────────────────────────────────────┘
@app. route ( "/" )
def index ( ) :
return render_template( "index.html" ,
title= "Flask模板渲染" ,
name= "Python爱好者" ,
items= [ "Jinja2模板引擎" , "变量插值" , "控制流" , "模板继承" ]
)
GET / → 200 | 响应长度: 286
✓ Jinja2模板渲染成功
✓ 变量插值正常
✓ for循环渲染正常
1.6 模板继承
< html>
< head> {% block title %}默认标题{% endblock %}</ head>
< body>
< nav> 导航栏</ nav>
{% block content %}{% endblock %}
< footer> 页脚</ footer>
</ body>
</ html>
{% extends "base.html" %}
{% block title %}首页 - Flask教程{% endblock %}
{% block content %}
< h2> 欢迎</ h2>
{% endblock %}
1.7 前后端架构对比
┌────────────────────────────────────────────────┐
│ Web 应用架构层次 │
├────────────────────────────────────────────────┤
│ 1-Tier (单层): 浏览器 + 业务逻辑 + 数据库一体 │
│ 示例: 单机Flask + SQLite │
├────────────────────────────────────────────────┤
│ 2-Tier (双层): 浏览器(客户端) ←→ Flask(服务端) │
│ 示例: 浏览器 → Flask → PostgreSQL│
├────────────────────────────────────────────────┤
│ 3-Tier (三层): 表现层 → 业务逻辑层 → 数据层 │
│ 示例: Vue → Flask → PostgreSQL │
└────────────────────────────────────────────────┘
前后端计算对比 :
维度 前端 (JavaScript) 后端 (Python) 执行位置 浏览器 服务器 用户可见代码 是 否 适用场景 交互/展示 数据/安全
后端计算: 1+2+...+100 = 5050
n=1000 → sum=500500 | 公式: n(n+1)/2 = 1000*1001/2
1.8 Request / Response 详解
@app. route ( "/inspect" , methods= [ "GET" , "POST" , "PUT" , "DELETE" ] )
def inspect ( ) :
info = {
"method" : request. method,
"url" : request. url,
"path" : request. path,
"host" : request. host,
"remote_addr" : request. remote_addr,
"headers" : dict ( request. headers) ,
"args" : dict ( request. args) ,
"form" : dict ( request. form) ,
"json" : request. get_json( ) ,
}
return jsonify( info)
Method: GET
Params: {'page': '1', 'q': 'test'}
Remote: 127.0.0.1
POST JSON: {'action': 'test', 'user': 'alice'}
1.9 表单与异常处理
GET vs POST 对比 :
特性 GET POST 数据位置 URL参数 请求体 安全性 低(URL可见) 高(不在URL) 数据大小 ~2KB 限制 无限制 幂等性 是 否 缓存 可缓存 不可缓存
异常处理 :
@app. route ( "/calc" , methods= [ "POST" ] )
def calculator ( ) :
try :
a = request. form. get( "a" , type = float )
b = request. form. get( "b" , type = float )
if op == "/" and b == 0 :
raise ZeroDivisionError( "除数不能为零" )
except ( TypeError, ValueError) as e:
return f"输入格式错误: { e} " , 400
except ZeroDivisionError as e:
return str ( e) , 400
POST /calc 10/3 → 200 | 结果: 3.333...
POST /calc 10/0 → 200 | 异常: 除数不能为零
二、Flask 数据库篇
2.1 PostgreSQL 连接
┌──────────┐ psycopg ┌──────────────┐
│ Flask │ ◄──────────────► │ PostgreSQL 16 │
│ 应用 │ conninfo │ (python_db) │
└──────────┘ └──────────────┘
import psycopg
DB_URL = "host=localhost dbname=python_db user=python_user password=Python@123"
with psycopg. connect( DB_URL) as conn:
with conn. cursor( ) as cur:
cur. execute( "SELECT version()" )
version = cur. fetchone( ) [ 0 ]
✓ PostgreSQL连接成功
版本: PostgreSQL 16.14
数据库: python_db | 用户: python_user
2.2 CRUD 操作速查
操作 SQL Python (psycopg3) 建表 CREATE TABLE users (...)cur.execute("CREATE TABLE...")插入 INSERT INTO users VALUES (...)cur.execute("INSERT...", (val1, val2))查询 SELECT * FROM userscur.execute("SELECT..."); cur.fetchall()更新 UPDATE users SET ...cur.execute("UPDATE...", (val, id))删除 DELETE FROM users WHERE id=?cur.execute("DELETE...", (id,))搜索 SELECT ... WHERE name LIKE '%q%'同上,参数化 %s 排序 ORDER BY col ASC/DESC同上 分页 LIMIT n OFFSET m同上
2.3 注册 + 登录
cur. execute(
"INSERT INTO flask_users (username, email, password_hash) VALUES (%s, %s, %s)" ,
( username, email, hashed_password)
)
conn. commit( )
cur. execute(
"SELECT * FROM flask_users WHERE username = %s AND password_hash = %s" ,
( username, hashed_password)
)
user = cur. fetchone( )
✓ 创建表 flask_users
✓ 插入 3 条用户记录
#1 alice alice@example.com
#2 bob bob@example.com
#3 charlie charlie@example.com
⚠ 重复注册检测: duplicate key value violates unique constraint
2.4 数据库连接池 (ConnectionPool)
from psycopg_pool import ConnectionPool
pool = ConnectionPool( DB_URL, min_size= 2 , max_size= 10 , open = True )
with pool. connection( ) as conn:
with conn. cursor( ) as cur:
cur. execute( "SELECT count(*) FROM flask_users" )
count = cur. fetchone( ) [ 0 ]
✓ 连接池创建: min=2, max=10
通过连接池查询: 用户总数 = 3
并发查询结果: id=1 → alice | id=3 → charlie | id=2 → bob
2.5 Cookie / Session 会话管理
┌──────────────────────────────────────────────┐
│ 会话管理流程 │
│ │
│ 用户 → 登录 → Session["username"]="alice" │
│ ↓ │
│ 浏览器 ← Set-Cookie: session=xxx │
│ ↓ │
│ 后续请求自动携带 Cookie → 服务端识别用户 │
│ ↓ │
│ 登出 → Session.pop("username") │
│ → Delete-Cookie │
└──────────────────────────────────────────────┘
app. secret_key = secrets. token_hex( 32 )
app. config[ 'PERMANENT_SESSION_LIFETIME' ] = timedelta( minutes= 30 )
@app. route ( "/login" )
def login ( ) :
session[ "username" ] = "alice"
session. permanent = True
@app. route ( "/" )
def index ( ) :
if "username" in session:
return jsonify( { "status" : "logged_in" , "username" : session[ "username" ] } )
GET / (未登录) → {"status": "not_logged_in"}
GET /login?user=alice → {"status": "login_ok"}
GET / (已登录) → {"status": "logged_in", "username": "alice"}
✓ Session保持登录状态成功
Session过期时间: 30分钟
GET /logout → {"status": "logged_out"}
2.6 批量操作
users = [ ( f"user_ { i: 02d } " , f"u { i: 02d } @example.com" ) for i in range ( 1 , 6 ) ]
cur. executemany(
"INSERT INTO flask_users (username, email, password_hash) VALUES (%s, %s, %s)" ,
[ ( u, e, f"hash_ { u} " ) for u, e in users]
)
conn. commit( )
✓ executemany 批量插入 5 条用户, 耗时: 0.0076s
逐条插入 5 条用户, 耗时: 0.0079s
性能对比: executemany 快约 1.0x (数据量小时差异不明显)
2.7 模糊搜索 + 排序
cur. execute(
"SELECT * FROM flask_users WHERE username LIKE %s ORDER BY username" ,
( f"% { search_term} %" , )
)
cur. execute( "SELECT username FROM flask_users ORDER BY username ASC" )
cur. execute( "SELECT username FROM flask_users ORDER BY username DESC" )
LIKE '%li%' 搜索结果: 2 条
#1 alice #6 charlie
ASC排序: alice → bob → charlie → diana → eve → frank
DESC排序: frank → eve → diana → charlie → bob → alice
2.8 分页(Pagination)
page, size = 1 , 5
offset = ( page - 1 ) * size
cur. execute( "SELECT count(*) FROM users" )
total = cur. fetchone( ) [ 0 ]
total_pages = ( total + size - 1 ) // size
cur. execute(
"SELECT * FROM users ORDER BY id LIMIT %s OFFSET %s" ,
( size, offset)
)
users = cur. fetchall( )
总记录数: 16
页码 每页 偏移 记录范围 数据
1 5 0 #1-#5 alice, bob, charlie, diana, eve
2 5 5 #6-#10 frank, user_01, user_02, user_03, user_04
3 5 10 #11-#15 user_05, user_06, user_07, user_08, user_09
1 10 0 #1-#10 alice, bob, charlie, diana, eve, frank, ...
总页数(每页5条): 4
总页数(每页10条): 2
三、AJAX 异步技术 + REST API
3.1 REST API 设计规范
REST API vs CRUD 对照表:
操作 HTTP方法 URL
────────────────────────────────────
查询全部 GET /api/users
查询单个 GET /api/users/<id>
创建 POST /api/users
更新 PUT /api/users/<id>
删除 DELETE /api/users/<id>
3.2 完整 AJAX CRUD 应用
@app. route ( "/api/users" , methods= [ "GET" ] )
def list_users ( ) :
search = request. args. get( "q" , "" )
page = request. args. get( "page" , 1 , type = int )
size = request. args. get( "size" , 10 , type = int )
return jsonify( {
"data" : users,
"total" : total,
"page" : page,
"pages" : ( total + size - 1 ) // size
} )
@app. route ( "/api/users/<int:user_id>" , methods= [ "DELETE" ] )
def delete_user ( user_id) :
cur. execute( "DELETE FROM users WHERE id = %s RETURNING *" , ( user_id, ) )
return jsonify( { "deleted" : row} )
前端 — fetch() API :
async function createUser ( ) {
const resp = await fetch ( "/api/users" , {
method : "POST" ,
headers : { "Content-Type" : "application/json" } ,
body : JSON . stringify ( { username, email} )
} ) ;
const data = await resp. json ( ) ;
}
GET /api/users → 200 | total=16
POST /api/users → 201 | id=20 username=ajax_user_01
PUT /api/users/20 → 200 | email→updated@test.com
DELETE /api/users/20 → 200 | deleted
GET /api/users?q=alice → 1 results
JSON导出: 16 条记录
3.3 fetch() vs XMLHttpRequest
特性 fetch() XMLHttpRequest 语法 fetch(url).then()new XMLHttpRequest()Promise 支持 ✓ 原生 Promise ✗ 需回调/封装 async/await ✓ 完美配合 ✗ 需手动包装 请求取消 AbortController xhr.abort()
3.4 AJAX 请求头
Content-Type: application/json
Accept: application/json
X-Requested-With: XMLHttpRequest (传统Ajax标识,fetch可选)
四、FastAPI 框架
4.1 最小应用
from fastapi import FastAPI
app = FastAPI(
title= "FastAPI教程" ,
version= "1.0.0"
)
@app. get ( "/" )
async def root ( ) :
return { "message" : "Hello, FastAPI!" }
if __name__ == "__main__" :
import uvicorn
uvicorn. run( app, host= "0.0.0.0" , port= 8000 )
GET / → 200 | {"message":"Hello, FastAPI!","framework":"FastAPI"}
GET /docs → 200 (Swagger UI 自动生成)
GET /openapi.json → 200 | schema size: 503 chars
4.2 Flask vs FastAPI 全面对比
特性 Flask FastAPI 协议 WSGI(同步) ASGI(异步) 类型提示 可选 强类型(Pydantic) API 文档 需 Flask-RESTX 自动 Swagger/ReDoc 数据验证 手动/Flask-Marsh Pydantic 自动 异步支持 有限(3.x+) 原生 async/await 性能 中等 高(媲美 Go/Node) 学习曲线 低 中 生态成熟度 非常成熟 快速增长 适用场景 传统 Web/模板 API 服务/微服务
4.3 Pydantic 数据模型
from pydantic import BaseModel, Field
class UserCreate ( BaseModel) :
username: str = Field( . . . , min_length= 2 , max_length= 50 )
email: Optional[ str ] = Field( None , pattern= r"^[\w\.-]+@[\w\.-]+\.\w+$" )
age: Optional[ int ] = Field( None , ge= 0 , le= 150 )
class UserResponse ( BaseModel) :
id : int
username: str
email: Optional[ str ] = None
created_at: datetime
4.4 完整 CRUD(带类型验证)
@app. get ( "/users" , response_model= List[ UserResponse] )
async def list_users (
q: Optional[ str ] = Query( None , description= "搜索关键词" ) ,
skip: int = Query( 0 , ge= 0 ) ,
limit: int = Query( 10 , ge= 1 , le= 100 ) ,
order: str = Query( "id" )
) : . . .
@app. post ( "/users" , response_model= UserResponse, status_code= 201 )
async def create_user ( user: UserCreate) : . . .
@app. put ( "/users/{user_id}" , response_model= UserResponse)
async def update_user ( user_id: int , user: UserUpdate) : . . .
@app. delete ( "/users/{user_id}" )
async def delete_user ( user_id: int ) : . . .
GET /users → 200 | 2 users
#1 alice age=25
#2 bob age=30
POST /users → 201 | charlie id=3
PUT /users/3 → 200 | email→charlie_new@test.com
DELETE /users/3 → 200 | Deleted
POST (验证失败) → 422:
username: String should have at least 2 characters
age: Input should be less than or equal to 150
4.5 路径参数 vs 查询参数
维度 Path Parameter Query Parameter URL 位置 /users/{user_id}/users?page=1&size=10是否必须 是(默认) 否(可设默认值) 用于 资源标识 过滤/排序/分页 FastAPI 语法 Path(...)Query(...)类型 int/str/uuid/path int/str/float/bool 示例 /posts/42/posts?tag=python
4.6 Query 参数校验选项
参数 类型 说明 defaultAny 默认值 ... (Ellipsis)— 标记为必填 min_lengthint 字符串最小长度 max_lengthint 字符串最大长度 pattern/regexstr 正则表达式匹配 ge (≥)int/float 数值最小值 le (≤)int/float 数值最大值 gt (>)int/float 大于 lt (<)int/float 小于 titlestr API 文档标题 descriptionstr API 文档描述 deprecatedbool 标记为弃用
@app. get ( "/search" )
async def search (
q: str = Query( . . . , min_length= 1 , max_length= 100 ) ,
page: int = Query( 1 , ge= 1 ) ,
size: int = Query( 10 , ge= 1 , le= 100 ) ,
sort: str = Query( "relevance" , pattern= "^(relevance|date|popular)$" ) ,
) : . . .
4.7 请求体 + 嵌套模型
class Address ( BaseModel) :
street: str
city: str
zipcode: Optional[ str ] = None
class UserCreate ( BaseModel) :
username: str = Field( . . . , min_length= 3 , max_length= 50 )
tags: List[ str ] = Field( default_factory= list )
address: Optional[ Address] = None
@app. post ( "/users" , status_code= 201 )
async def create_user ( user: UserCreate) :
return { "created" : user. model_dump( ) }
POST /users (嵌套模型) → 201
username: john
address: {"street":"Main St 123","city":"Shenzhen"}
POST /users/bulk → 201 | 3 users created
4.8 Pydantic Field 校验
参数 说明 示例 ...必填 Field(...)default默认值 Field("default")min_length最小长度 Field(..., min_length=2)max_length最大长度 Field(..., max_length=50)ge/le/gt/lt数值范围 Field(..., ge=0, le=100)pattern/regex正则匹配 Field(..., pattern=r"\d+")examples示例值 Field(..., examples=["x"])
五、踩坑记录
# 问题 原因 解决方案 1 blinker 卸载失败 Debian 安装的 blinker 缺少 RECORD pip install --ignore-installed blinker2 render_template_string(''' 嵌套外层 ''' 和内层 ''' 冲突 外层用 """,内层用 '''(或反之) 3 密码含 @ 被 SQLAlchemy URL 误解析 URL 中 @ 同时是密码和分隔符 使用 %40 URL 编码 4 executemany 占位符与参数数量不匹配 WHERE IN (%s, %s) 但只传一个参数 IN 中使用动态占位符 5 psycopg_pool 单独安装 psycopg_pool 是独立包 pip install psycopg_pool6 Session 过期未生效 默认 session 非持久 设置 app.config['PERMANENT_SESSION_LIFETIME']
附录:完整架构图
┌─────────────────────────────────────────────────────────┐
│ Web 后端技术栈 │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 前端 (SPA) │ │ 前端 (SSR) │ │
│ │ Vue/React/Angular│ │ Jinja2 模板 │ │
│ └────────┬────────┘ └────────┬────────┘ │
│ │ AJAX/fetch │ SSR │
│ ▼ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ Web 框架层 │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ Flask │ │ FastAPI │ │ │
│ │ │ (WSGI) │ │ (ASGI) │ │ │
│ │ ├──────────┤ ├──────────────┤ │ │
│ │ │ Jinja2 │ │ Swagger/ReDoc│ │ │
│ │ │ Session │ │ Pydantic │ │ │
│ │ │ Blueprint│ │ async/await │ │ │
│ │ └──────────┘ └──────────────┘ │ │
│ └────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ 数据访问层 │ │
│ │ ┌──────────┐ ┌──────────────┐ │ │
│ │ │ psycopg3 │ │ SQLAlchemy │ │ │
│ │ │ Connection│ │ ORM │ │ │
│ │ │ Pool │ │ Core │ │ │
│ │ └──────────┘ └──────────────┘ │ │
│ └────────────────┬───────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────────────────────┐ │
│ │ PostgreSQL 16 │ │
│ │ (python_db) │ │
│ └────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
系列已发布 7 篇 :python3从零开始(58KB) → 深入编解码(36KB) → 文本与NLP(37KB) → Office(24KB) → 数据库(20KB) → 网络爬虫(33KB) → Web后端技术(本文) 累计 : 621 实验 / 实战服务器: ecs-88e7-0001 (139.9.128.210) | Ubuntu 24.04.4 | Python 3.12.3