4. Flask 基础:框架介绍、路由与视图函数、GET 与 POST 请求处理

在精算模型上线流程中,Flask 是最轻量、最常用的 Python Web 框架之一。它允许我们将训练好的模型(如 GLM、GBM、神经网络)包装成一个 HTTP 服务,从而被前端应用、批处理调度器或下游系统调用。本章将系统讲解 Flask 的核心概念:框架定位、路由与视图函数的映射机制,以及 GET/POST 请求的处理方式。

4.1 Flask 框架概述

Flask 是一个基于 Werkzeug(WSGI 工具库)和 Jinja2(模板引擎)的微框架。其核心设计哲学是“微内核 + 扩展”,即框架本身只提供路由、请求/响应对象、会话管理等基础能力,数据库 ORM、表单验证、认证等功能通过第三方扩展按需集成。

特性 说明
轻量级 核心代码仅约 2000 行,无强制依赖,适合快速搭建 API 服务
路由灵活 支持动态 URL 参数、HTTP 方法绑定、蓝图模块化
开发模式 内置调试器与自动重载,开发效率高
生产就绪 配合 Gunicorn / uWSGI + Nginx 可承载高并发生产流量
模型服务场景 单进程加载模型,通过路由分发预测请求,响应延迟通常在 10~100ms 级别
精算场景提示: 在精算模型上线初期,Flask 常作为“模型代理层”使用——它不负责训练,只负责加载已序列化的模型(pickle / joblib / ONNX),接收输入特征并返回预测结果。这种架构将模型推理与业务逻辑解耦,便于后续替换为更高性能的推理框架(如 Triton Inference Server)。

4.2 路由与视图函数

路由(Route)是 URL 到 Python 函数的映射关系。Flask 使用 @app.route() 装饰器将视图函数绑定到指定 URL 路径。当客户端请求该 URL 时,Flask 自动调用对应的视图函数,并将返回值作为 HTTP 响应返回。

4.2.1 基本路由定义

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '精算模型服务已启动'

@app.route('/health')
def health_check():
    return {'status': 'healthy', 'model_version': 'v2.1.0'}

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)

4.2.2 动态路由与类型转换器

精算模型服务中,经常需要根据模型版本、保单 ID 或产品代码动态构造 URL。Flask 支持在路由中使用 <variable> 捕获 URL 片段,并可指定类型转换器:

转换器 说明 示例
string 默认,接受任意字符串(不含斜杠) /model/<string:name>
int 接受正整数 /policy/<int:policy_id>
float 接受浮点数 /score/<float:amount>
path 接受字符串(含斜杠) /files/<path:subpath>
uuid 接受 UUID 字符串 /session/<uuid:session_id>
@app.route('/api/v1/model/<string:model_name>/predict')
def predict_by_model(model_name):
    # 根据 model_name 加载对应模型并执行推理
    return {'model': model_name, 'prediction': 0.85}

@app.route('/api/v1/policy/<int:policy_id>')
def get_policy(policy_id):
    # 查询保单信息
    return {'policy_id': policy_id, 'premium': 12500.0}

4.2.3 路由与视图函数映射关系(SVG)

下图展示了客户端请求到达 Flask 服务后,路由匹配与视图函数调用的完整流程:

Flask 路由匹配与视图函数调用流程 客户端 HTTP 请求 WSGI 服务器 路由匹配 URL 映射表 /health → health_check /predict → predict_model /policy/<id> → get_policy 视图函数 HTTP 响应

4.3 GET 与 POST 请求处理

在模型服务中,GET 和 POST 是最常用的两种 HTTP 方法。GET 通常用于查询状态、获取元数据;POST 用于提交特征数据并获取预测结果。

4.3.1 方法对比

对比维度 GET POST
数据位置 URL 查询参数(?key=value) 请求体(Body),支持 JSON / Form / 文件
数据长度限制 受 URL 长度限制(通常 2KB~8KB) 无理论限制(受服务器配置约束)
安全性 参数暴露在 URL 中,不适合敏感数据 数据在请求体中,相对更安全
幂等性 幂等,多次请求结果相同 非幂等,每次可能改变服务状态
模型服务典型用途 健康检查、模型元信息查询、批量状态轮询 单条/批量预测、模型重载、特征上传

4.3.2 处理 GET 请求

Flask 默认路由只响应 GET 请求。通过 request.args 获取 URL 查询参数:

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/api/v1/score')
def score():
    # 从查询参数中获取特征值
    age = request.args.get('age', type=int)
    income = request.args.get('income', type=float)
    credit_score = request.args.get('credit_score', type=float)

    if None in (age, income, credit_score):
        return jsonify({'error': '缺少必要参数: age, income, credit_score'}), 400

    # 模拟模型预测
    risk_score = 0.3 * (age / 100) + 0.5 * (income / 100000) + 0.2 * (1 - credit_score / 1000)
    return jsonify({'risk_score': round(risk_score, 4)})

4.3.3 处理 POST 请求

POST 请求是模型推理的标准方式。通过 request.get_json() 解析 JSON 请求体:

@app.route('/api/v1/predict', methods=['POST'])
def predict():
    # 解析 JSON 请求体
    data = request.get_json()
    if not data:
        return jsonify({'error': '请求体必须为 JSON 格式'}), 400

    features = data.get('features')
    if not features:
        return jsonify({'error': '缺少 features 字段'}), 400

    # 模拟模型推理
    prediction = sum(features) / len(features) if features else 0
    return jsonify({
        'prediction': prediction,
        'model': 'demo_glm_v1',
        'status': 'success'
    })

4.3.4 GET 与 POST 请求处理对比(SVG)

下图展示了两种请求方法在模型服务中的数据处理路径差异:

GET 与 POST 请求处理路径对比 GET 请求 URL 查询参数 request.args 解析 返回 JSON 响应 POST 请求 JSON 请求体 request.get_json() 返回 JSON 响应 核心差异 • GET:数据在 URL 中,适合简单查询 • POST:数据在 Body 中,适合复杂特征 • 模型预测推荐使用 POST + JSON

4.4 综合示例:精算模型预测 API

以下代码整合了本章知识点,实现一个完整的精算模型预测服务:

from flask import Flask, request, jsonify
import joblib
import numpy as np

app = Flask(__name__)

# 模拟加载预训练模型(实际项目中替换为真实模型文件)
# model = joblib.load('models/glm_premium_v2.pkl')

@app.route('/')
def index():
    return '精算模型服务 v1.0'

@app.route('/api/v1/model/info', methods=['GET'])
def model_info():
    """GET 请求:返回模型元信息"""
    return jsonify({
        'model_name': 'GLM_Premium_v2',
        'version': '2.1.0',
        'features': ['age', 'vehicle_value', 'driving_exp', 'claim_history'],
        'target': 'annual_premium'
    })

@app.route('/api/v1/predict/premium', methods=['POST'])
def predict_premium():
    """POST 请求:接收特征数据,返回保费预测"""
    try:
        data = request.get_json()
        if not data:
            return jsonify({'error': '请求体为空'}), 400

        # 提取特征向量
        features = [
            data.get('age', 0),
            data.get('vehicle_value', 0),
            data.get('driving_exp', 0),
            data.get('claim_history', 0)
        ]

        # 模型推理(此处用模拟计算代替)
        # prediction = model.predict([features])[0]
        prediction = 5000 + 200 * features[0] + 0.05 * features[1] - 150 * features[2] + 300 * features[3]

        return jsonify({
            'predicted_premium': round(prediction, 2),
            'currency': 'CNY',
            'model_version': '2.1.0'
        })

    except Exception as e:
        return jsonify({'error': f'推理失败: {str(e)}'}), 500

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000, debug=True)
生产环境注意事项:
  • 生产环境中必须关闭 debug=True,使用 Gunicorn 或 uWSGI 作为 WSGI 服务器。
  • 模型加载应在应用启动时完成(全局变量),避免每次请求都重新加载模型。
  • 建议添加请求日志、超时控制、输入校验等中间件,防止恶意请求导致服务崩溃。
  • 对于高并发场景,可配合 Nginx 反向代理实现负载均衡。

4.5 本章小结

  • Flask 框架:轻量、灵活,适合精算模型服务的快速搭建与迭代。
  • 路由与视图函数:通过 @app.route() 将 URL 映射到 Python 函数,支持动态参数和类型转换器。
  • GET 请求:通过 request.args 获取查询参数,适用于状态查询和元数据获取。
  • POST 请求:通过 request.get_json() 解析 JSON 请求体,是模型推理的标准方式。
  • 生产化要点:关闭 debug、全局加载模型、添加输入校验和错误处理。