# Flask 实战 Flask 是一个轻量级的 WSGI Web 框架,以简单灵活著称,适合快速开发和小到中型项目。 ## 快速入门 ### 基础应用 ```python from flask import Flask, request, jsonify app = Flask(__name__) @app.route('/') def hello(): return 'Hello, World!' @app.route('/api/items/') def get_item(item_id): return jsonify({"id": item_id}) @app.route('/api/items', methods=['POST']) def create_item(): data = request.get_json() return jsonify(data), 201 if __name__ == '__main__': app.run(debug=True) ``` ### 应用工厂模式 ```python # app/__init__.py from flask import Flask from flask_sqlalchemy import SQLAlchemy from flask_migrate import Migrate db = SQLAlchemy() migrate = Migrate() def create_app(config_name='development'): app = Flask(__name__) # 加载配置 app.config.from_object(config[config_name]) # 初始化扩展 db.init_app(app) migrate.init_app(app, db) # 注册蓝图 from app.views import main_bp, api_bp app.register_blueprint(main_bp) app.register_blueprint(api_bp, url_prefix='/api') # 错误处理 register_error_handlers(app) return app def register_error_handlers(app): @app.errorhandler(404) def not_found(error): return jsonify({"error": "Not found"}), 404 @app.errorhandler(500) def internal_error(error): db.session.rollback() return jsonify({"error": "Internal server error"}), 500 ``` ## 蓝图(Blueprint) ```python # app/views/users.py from flask import Blueprint, request, jsonify from app.models import User from app.services import UserService users_bp = Blueprint('users', __name__) @users_bp.route('/', methods=['GET']) def list_users(): page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 20, type=int) pagination = User.query.paginate(page=page, per_page=per_page) return jsonify({ 'users': [u.to_dict() for u in pagination.items], 'total': pagination.total, 'pages': pagination.pages, 'current_page': page }) @users_bp.route('/', methods=['GET']) def get_user(user_id): user = User.query.get_or_404(user_id) return jsonify(user.to_dict()) @users_bp.route('/', methods=['POST']) def create_user(): data = request.get_json() # 验证 if not data.get('email'): return jsonify({"error": "Email required"}), 400 user = UserService.create_user(data) return jsonify(user.to_dict()), 201 ``` ## 请求处理 ### 获取请求数据 ```python from flask import request @app.route('/api/submit', methods=['POST']) def submit(): # URL 参数: /api/submit?page=1 page = request.args.get('page', 1, type=int) # JSON 数据 json_data = request.get_json() # 表单数据 form_data = request.form.get('field_name') # 文件上传 file = request.files.get('upload') if file: file.save(f'/uploads/{file.filename}') # 请求头 auth_header = request.headers.get('Authorization') # Cookies session_id = request.cookies.get('session_id') return jsonify({"status": "received"}) ``` ### 响应处理 ```python from flask import make_response, jsonify, redirect, url_for @app.route('/api/custom-response') def custom_response(): response = make_response(jsonify({"data": "value"})) response.headers['X-Custom-Header'] = 'custom-value' response.set_cookie('session', 'value', httponly=True, secure=True) return response @app.route('/redirect-example') def redirect_example(): return redirect(url_for('get_user', user_id=1)) @app.route('/api/download') def download(): data = generate_csv() response = make_response(data) response.headers['Content-Type'] = 'text/csv' response.headers['Content-Disposition'] = 'attachment; filename=data.csv' return response ``` ## 数据库操作 ### SQLAlchemy 模型 ```python # app/models/user.py from app import db from datetime import datetime from werkzeug.security import generate_password_hash, check_password_hash class User(db.Model): __tablename__ = 'users' id = db.Column(db.Integer, primary_key=True) username = db.Column(db.String(80), unique=True, nullable=False) email = db.Column(db.String(120), unique=True, nullable=False) password_hash = db.Column(db.String(128)) created_at = db.Column(db.DateTime, default=datetime.utcnow) # 关系 posts = db.relationship('Post', backref='author', lazy='dynamic') def set_password(self, password): self.password_hash = generate_password_hash(password) def check_password(self, password): return check_password_hash(self.password_hash, password) def to_dict(self): return { 'id': self.id, 'username': self.username, 'email': self.email, 'created_at': self.created_at.isoformat() } def __repr__(self): return f'' ``` ### 数据库查询 ```python from app.models import User # 基本查询 user = User.query.get(1) user = User.query.filter_by(username='alice').first() user = User.query.filter(User.email.like('%@example.com')).all() # 复杂查询 from sqlalchemy import and_, or_ users = User.query.filter( and_( User.created_at >= start_date, or_( User.username.contains('admin'), User.email.endswith('@company.com') ) ) ).order_by(User.created_at.desc()).limit(10).all() # 分页 pagination = User.query.paginate(page=1, per_page=20) users = pagination.items total = pagination.total # 聚合 from sqlalchemy import func count = db.session.query(func.count(User.id)).scalar() ``` ### 事务处理 ```python from app import db def transfer_funds(from_id, to_id, amount): try: from_account = Account.query.get(from_id) to_account = Account.query.get(to_id) if from_account.balance < amount: raise ValueError("Insufficient funds") from_account.balance -= amount to_account.balance += amount db.session.commit() except Exception as e: db.session.rollback() raise ``` ## 认证与授权 ### Flask-Login 集成 ```python from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user login_manager = LoginManager() class User(UserMixin, db.Model): # UserMixin 提供 is_authenticated, is_active, is_anonymous, get_id pass @login_manager.user_loader def load_user(user_id): return User.query.get(int(user_id)) @app.route('/login', methods=['POST']) def login(): data = request.get_json() user = User.query.filter_by(email=data['email']).first() if user and user.check_password(data['password']): login_user(user, remember=data.get('remember', False)) return jsonify({"message": "Logged in"}) return jsonify({"error": "Invalid credentials"}), 401 @app.route('/logout') @login_required def logout(): logout_user() return jsonify({"message": "Logged out"}) @app.route('/profile') @login_required def profile(): return jsonify(current_user.to_dict()) ``` ### JWT 认证 ```python from flask import Flask, request, jsonify from functools import wraps import jwt from datetime import datetime, timedelta app = Flask(__name__) app.config['SECRET_KEY'] = 'your-secret-key' def token_required(f): @wraps(f) def decorated(*args, **kwargs): token = request.headers.get('Authorization', '').replace('Bearer ', '') if not token: return jsonify({'error': 'Token missing'}), 401 try: data = jwt.decode(token, app.config['SECRET_KEY'], algorithms=['HS256']) current_user = User.query.get(data['user_id']) except jwt.ExpiredSignatureError: return jsonify({'error': 'Token expired'}), 401 except jwt.InvalidTokenError: return jsonify({'error': 'Invalid token'}), 401 return f(current_user, *args, **kwargs) return decorated @app.route('/api/login', methods=['POST']) def api_login(): data = request.get_json() user = User.query.filter_by(email=data['email']).first() if user and user.check_password(data['password']): token = jwt.encode({ 'user_id': user.id, 'exp': datetime.utcnow() + timedelta(hours=24) }, app.config['SECRET_KEY']) return jsonify({'token': token}) return jsonify({'error': 'Invalid credentials'}), 401 @app.route('/api/protected') @token_required def protected(current_user): return jsonify({'user': current_user.to_dict()}) ``` ## 中间件和钩子 ```python from flask import g, request import time # 请求前 @app.before_request def before_request(): g.start_time = time.time() g.user = None token = request.headers.get('Authorization') if token: g.user = verify_token(token) # 请求后 @app.after_request def after_request(response): # 添加响应头 response.headers['X-Process-Time'] = str(time.time() - g.start_time) return response # 请求结束(无论成功失败) @app.teardown_request def teardown_request(exception=None): db = getattr(g, '_database', None) if db is not None: db.close() # 第一次请求前 @app.before_first_request def before_first_request(): # 初始化操作 pass ``` ## 测试 ```python # tests/test_api.py import pytest from app import create_app, db from app.models import User @pytest.fixture def app(): app = create_app('testing') with app.app_context(): db.create_all() yield app db.drop_all() @pytest.fixture def client(app): return app.test_client() @pytest.fixture def auth_header(client): # 创建测试用户 response = client.post('/api/register', json={ 'email': 'test@example.com', 'password': 'password123' }) response = client.post('/api/login', json={ 'email': 'test@example.com', 'password': 'password123' }) token = response.get_json()['token'] return {'Authorization': f'Bearer {token}'} def test_get_users(client, auth_header): response = client.get('/api/users', headers=auth_header) assert response.status_code == 200 def test_create_user(client): response = client.post('/api/users', json={ 'email': 'new@example.com', 'username': 'newuser' }) assert response.status_code == 201 assert response.get_json()['email'] == 'new@example.com' ``` ## 部署 ### Gunicorn ```bash # 安装 pip install gunicorn # 运行 gunicorn -w 4 -b 0.0.0.0:8000 "app:create_app()" # 生产配置 gunicorn \ --workers 4 \ --threads 2 \ --worker-class gthread \ --bind 0.0.0.0:8000 \ --access-logfile - \ --error-logfile - \ "app:create_app('production')" ``` ### Docker ```dockerfile FROM python:3.11-slim WORKDIR /app COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt COPY . . EXPOSE 8000 CMD ["gunicorn", "-w", "4", "-b", "0.0.0.0:8000", "app:create_app()"] ``` ## 最佳实践 ::::{grid} 1 :gutter: 2 :::{grid-item-card} 项目组织 1. **使用应用工厂**:便于测试和配置 2. **使用蓝图**:模块化路由 3. **分离配置**:不同环境不同配置 4. **使用服务层**:业务逻辑与视图分离 ::: :::{grid-item-card} 安全实践 1. **永远不要信任用户输入** 2. **使用 CSRF 保护**(flask-wtf) 3. **设置安全的 Cookie**(httponly, secure) 4. **使用环境变量存储密钥** 5. **定期更新依赖** ::: :::{grid-item-card} 性能优化 1. **使用数据库索引** 2. **实现分页** 3. **使用缓存**(Flask-Caching) 4. **异步任务**(Celery) 5. **连接池** ::: ::::