• [技术干货] Flask常见应用部署方案详解【转载】
    开发调试阶段,运行 Flask 的方式多直接使用 app.run(),但 Flask 内置的 WSGI Server 的性能并不高。对于生产环境,一般使用 gunicorn。如果老项目并不需要多高的性能,而且用了很多单进程内的共享变量,使用 gunicorn 会影响不同会话间的通信,那么也可以试试直接用 gevent。在 Docker 流行之前,生产环境部署 Flask 项目多使用 virtualenv + gunicorn + supervisor。Docker 流行之后,部署方式就换成了 gunicorn + Docker。如果没有容器编排服务,后端服务前面一般还会有个 nginx 做代理。如果使用 Kubernetes,一般会使用 service + ingress(或 istio 等)。运行方式Flask 内置 WSGI Server开发阶段一般使用这种运行方式。12345678910111213# main.pyfrom flask import Flaskfrom time import sleep app = Flask(__name__) @app.get("/test")def get_test():    sleep(0.1)    return "ok" if __name__ == "__main__":    app.run(host="0.0.0.0", port=10000)运行:1python main.pygevent使用 gevent 运行 Flask,需要先安装 gevent1python -m pip install -U gevent代码需要稍作修改。需要注意 monkey.patch_all() 一定要写在入口代码文件的最开头部分,这样 monkey patch 才能生效。123456789101112131415161718192021# main.pyfrom gevent import monkeymonkey.patch_all()import time from flask import Flaskfrom gevent.pywsgi import WSGIServer  app = Flask(__name__)  @app.get("/test")def get_test():    time.sleep(0.1)    return "ok"  if __name__ == "__main__":    server = WSGIServer(("0.0.0.0", 10000), app)    server.serve_forever()运行1python main.pygunicorn + gevent如果现有项目大量使用单进程内的内存级共享变量,贸然使用 gunicorn 多 worker 模式可能会导致数据访问不一致的问题。同样需要先安装依赖。1python -m pip install -U gunicorn gevent不同于单独使用 gevent,这种方式不需要修改代码,gunicorn 会自动注入 gevent 的 monkey patch。gunicorn 可以在命令行配置启动参数,但个人一般习惯在 gunicorn 的配置文件内配置启动参数,这样可以动态设置一些配置,而且可以修改日志格式。gunicorn.conf.py 的配置示例如下:1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768# Gunicorn 配置文件from pathlib import Pathfrom multiprocessing import cpu_countimport gunicorn.gloggingfrom datetime import datetime class CustomLogger(gunicorn.glogging.Logger):    def atoms(self, resp, req, environ, request_time):        """        重写 atoms 方法来自定义日志占位符        """        # 获取默认的所有占位符数据        atoms = super().atoms(resp, req, environ, request_time)                 # 自定义 't' (时间戳) 的格式        now = datetime.now().astimezone()        atoms['t'] = now.isoformat(timespec="seconds")                 return atoms      # 预加载应用代码preload_app = True # 工作进程数量:通常是 CPU 核心数的 2 倍加 1# workers = int(cpu_count() * 2 + 1)workers = 4 # 使用 gevent 异步 worker 类型,适合 I/O 密集型应用# 注意:gevent worker 不使用 threads 参数,而是使用协程进行并发处理worker_class = "gevent" # 每个 gevent worker 可处理的最大并发连接数worker_connections = 2000 # 绑定地址和端口bind = "127.0.0.1:10001" # 进程名称proc_name = "flask-dev" # PID 文件路径pidfile = str(Path(__file__).parent / "tmp" / "gunicorn.pid") logger_class = CustomLoggeraccess_log_format = (    '{"@timestamp": "%(t)s", '    '"remote_addr": "%(h)s", '    '"protocol": "%(H)s", '    '"host": "%({host}i)s", '    '"request_method": "%(m)s", '    '"request_path": "%(U)s", '    '"status_code": %(s)s, '    '"response_length": %(b)s, '    '"referer": "%(f)s", '    '"user_agent": "%(a)s", '    '"x_tracking_id": "%({x-tracking-id}i)s", '    '"request_time": %(L)s}') # 访问日志路径accesslog = str(Path(__file__).parent / "logs" / "access.log") # 错误日志路径errorlog = str(Path(__file__).parent / "logs" / "error.log") # 日志级别loglevel = "debug"运行。gunicorn 的默认配置文件名就是 gunicorn.conf.py,如果文件名不同,可以使用 -c 参数来指定。1gunicorn main:app传统进程管理:实现自动启动在传统服务器部署时,常见的进程守护方式有:配置 crontab + shell 脚本。定时检查进程在不在,不在就启动。配置 supervisor。配置 systemd。由于 supervisor 需要单独安装,而本着能用自带工具就用自带工具、能少装就少装的原则,个人一般不会使用 supervisor,因此本文不会涉及如何使用 supervisor。在服务器部署时,一般也会为项目单独创建 Python 虚拟环境。123456# 使用 Python 内置的 venv,在当前目录创建 Python 虚拟环境目录 .venvpython3 -m venv .venvsource .venv/bin/activatepython -m pip install -r ./requirements.txt # 如果使用uv, 直接uv sync 即可crontab + shell 脚本 (不推荐生产环境)刚入行的时候对 systemd 不熟悉,经常用 crontab + shell 脚本来守护进程,现在想想这种方式并不合适,比较考验 shell 脚本的编写水平,需要考虑方方面面首先要确保用户级 crontab 启用,有些生产环境会禁用用户级的 crontab,而且也不允许随便配置系统级的 crontab。crontab 是分钟级的,服务停止时间可能要一分钟。如果有控制台日志,需要手动处理日志重定向,还有日志文件轮转问题。如果 ulimit 不高,还得控制 ulimit。经常出现僵尸进程,shell 脚本来要写一堆状态检查的逻辑。如果只需要简单用用,也可以提供个示例123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150#!/bin/bash # 环境配置export FLASK_ENV="production"export DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"export REDIS_URL="redis://localhost:6379/0" script_dir=$(cd $(dirname $0) && pwd)app_name="gunicorn"  # 实际进程名是 gunicorn,不是 Flask appwsgi_module="wsgi:app"  # 替换 WSGI 入口socket_path="${script_dir}/myapp.sock"  # Unix Socket 路径(避免 /run 重启丢失)log_file="${script_dir}/app.log"pid_file="${script_dir}/gunicorn.pid"   # 用 PID 文件控制 # 进程检测is_running() {    if [ -f "$pid_file" ]; then        pid=$(cat "$pid_file")        if ps -p "$pid" > /dev/null 2>&1 && grep -q "gunicorn.*${wsgi_module}" /proc/"$pid"/cmdline 2>/dev/null; then            echo "Gunicorn (PID: $pid) is running"            return 0        else            rm -f "$pid_file"  # 清理失效 PID            echo "Stale PID file found, cleaned up"            return 1        fi    else        # 备用检测:通过 socket 文件 + 进程名        if [ -S "$socket_path" ] && pgrep -f "gunicorn.*${wsgi_module}" > /dev/null 2>&1; then            echo "Gunicorn is running (detected by socket)"            return 0        fi        echo "Gunicorn is not running"        return 1    fi} # 启动应用start_app() {    is_running    if [ $? -eq 0 ]; then        echo "Already running, skip start"        return 0    fi     echo "Starting Gunicorn at $(date)"    echo "Socket: $socket_path"    echo "Log: $log_file"     # 确保 socket 目录存在    mkdir -p "$(dirname "$socket_path")"     # 启动命令(关键:不加 --daemon,用 nohup 托管)    cd "$script_dir" || exit 1    # 生成 PID 文件    nohup "$script_dir/venv/bin/gunicorn" \        --workers 3 \        --bind "unix:$socket_path" \        --pid "$pid_file" \        --access-logfile "$log_file" \        --error-logfile "$log_file" \        --log-level info \        "$wsgi_module" > /dev/null 2>&1 &     # 等待启动完成    sleep 2    if is_running; then        echo "✓ Start success (PID: $(cat "$pid_file" 2>/dev/null))"        return 0    else        echo "✗ Start failed, check $log_file"        return 1    fi} # 停止应用stop_app() {    is_running    if [ $? -eq 1 ]; then        echo "Not running, skip stop"        return 0    fi     pid=$(cat "$pid_file" 2>/dev/null)    echo "Stopping Gunicorn (PID: $pid) gracefully..."     # 先发 SIGTERM(优雅停止)    kill -15 "$pid" 2>/dev/null || true    sleep 5     # 检查是否还在运行    if ps -p "$pid" > /dev/null 2>&1; then        echo "Still running after 5s, force killing..."        kill -9 "$pid" 2>/dev/null || true        sleep 2    fi     # 清理残留    rm -f "$pid_file" "$socket_path"    echo "✓ Stopped"} # 重启应用restart_app() {    echo "Restarting Gunicorn..."    stop_app    sleep 1    start_app} # 入口函数main() {    # 检查 Gunicorn 是否存在    if [ ! -f "$script_dir/venv/bin/gunicorn" ]; then        echo "ERROR: Gunicorn not found at $script_dir/venv/bin/gunicorn"        echo "Hint: Did you activate virtualenv? (source venv/bin/activate)"        exit 1    fi     local action=${1:-start}  # 默认动作:start     case "$action" in        start)            start_app            ;;        stop)            stop_app            ;;        restart)            restart_app            ;;        status)            is_running            ;;        cron-check)            # 专为 crontab 设计:只检查+重启,不输出干扰日志            if ! is_running > /dev/null 2>&1; then                echo "[$(date '+%F %T')] CRON: Gunicorn down, auto-restarting..." >> "$log_file"                start_app >> "$log_file" 2>&1            fi            ;;        *)            echo "Usage: $0 {start|stop|restart|status|cron-check}"            echo "  cron-check: Silent mode for crontab (logs to app.log only)"            exit 1            ;;    esac} main "$@"手动运行测试1bash app_ctl.sh start配置 crontab12345# 编辑当前用户 crontabcrontab -e # 添加以下行(每分钟检查一次)* * * * * /opt/myflaskapp/app_ctl.sh cron-check >/dev/null 2>&1配置logrotate12345678910# /etc/logrotate.d/myflaskapp/opt/myflaskapp/app.log {    daily    rotate 7    compress    delaycompress    missingok    notifempty    copytruncate  # 避免 Gunicorn 丢失文件句柄}systemd (推荐生产环境使用)创建 systemd 服务文件1sudo vim /etc/systemd/system/myflaskapp.service示例如下12345678910111213141516171819202122232425262728293031[Unit]Description=Gunicorn instance for Flask AppAfter=network.target [Service]User=www-dataGroup=www-dataWorkingDirectory=/path/to/your/appEnvironment="PATH=/path/to/venv/bin"ExecStart=/path/to/venv/bin/gunicorn \          --workers 4 \          --bind unix:/run/myapp.sock \          --access-logfile - \          --error-logfile - \          wsgi:app # 禁止添加 --daemon!systemd 需直接监控主进程Restart=on-failure        # 仅异常退出时重启(非0状态码、被信号杀死等)RestartSec=5s             # 重启前等待5秒StartLimitInterval=60s    # 60秒内StartLimitBurst=5         # 最多重启5次,防雪崩TimeoutStopSec=30         # 停止时等待30秒(优雅关闭) # 安全加固PrivateTmp=trueNoNewPrivileges=trueProtectSystem=strictReadWritePaths=/run /var/log/myapp [Install]WantedBy=multi-user.target设置开机自启并启动服务123sudo systemctl daemon-reloadsudo systemctl enable myflaskapp    # 开机自启sudo systemctl start myflaskapp可以试试用kill -9停止后端服务进程,观察能否被重新拉起。注意,kill -15算是正常停止,不算异常退出。Docker 部署方案Dockerfile。Python 项目通常不需要多阶段构建,单阶段即可。12345678910111213141516171819202122232425262728293031323334353637FROM python:3.11-slim-bookworm # 安全加固## 创建非 root 用户(避免使用 nobody,权限太受限)RUN useradd -m -u 1000 appuser && \    # 安装运行时必需的系统库(非编译工具)    apt-get update && apt-get install -y --no-install-recommends \        libgomp1 \        libpq5 \        libsqlite3-0 \        && rm -rf /var/lib/apt/lists/* \        && apt-get autoremove -y \        && apt-get clean # Python 优化ENV PYTHONUNBUFFERED=1 \    PYTHONDONTWRITEBYTECODE=1 \    PIP_NO_CACHE_DIR=1 \    PIP_DISABLE_PIP_VERSION_CHECK=1 WORKDIR /app # 利用 Docker 层缓存:先复制 requirementsCOPY requirements.txt .RUN pip install --no-cache-dir --prefer-binary -r requirements.txt \    # 清理 pip 缓存(虽然 --no-cache-dir 已禁用,但保险起见)    && rm -rf /root/.cache # 应用代码COPY --chown=appuser:appuser . . # 使用非root用户运行USER appuser # 启动EXPOSE 8000CMD ["gunicorn", "--config", "config/gunicorn.conf.py", "wsgi:app"]编写 docker-compose.yaml1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071services:  web:    image: myflaskapp:latest    container_name: flask_web    # 端口映射    ## 如果 nginx 也使用 Docker 部署,而且使用同一个网络配置,则可以不做端口映射    ports:      - "8000:8000"    # 环境变量    environment:      - FLASK_ENV=production      - DATABASE_URL=postgresql://user:pass@db:5432/mydb      - REDIS_URL=redis://redis:6379/0    # 健康检查    healthcheck:      test: ["CMD", "curl", "-f", "http://localhost:8000/health"]      interval: 30s      # 每 30 秒检查一次      timeout: 5s        # 超时 5 秒      start_period: 15s  # 启动后 15 秒开始检查(给应用初始化时间)      retries: 3         # 失败重试 3 次后标记 unhealthy         # 自动重启策略    restart: unless-stopped  # always / on-failure / unless-stopped         # 资源限制    deploy:      resources:        limits:          cpus: '2'        # 最多 2 个 CPU          memory: 1G       # 最多 1GB 内存        reservations:          cpus: '0.5'      # 保留 0.5 个 CPU          memory: 256M     # 保留 256MB 内存         # ulimit 限制(防资源滥用)    ulimits:      nproc: 65535       # 最大进程数      nofile:        soft: 65535      # 打开文件数软限制        hard: 65535      # 打开文件数硬限制      core: 0            # 禁止 core dump         # 安全加固    security_opt:      - no-new-privileges:true  # 禁止提权         # 只读文件系统(除 /tmp 外)    read_only: true    tmpfs:      - /tmp:rw,noexec,nosuid,size=100m         # 卷挂载(日志、临时文件)    volumes:      - ./logs:/app/logs:rw      # - ./static:/app/static:ro  # 静态文件(可选)         # 网络    networks:      - app-network         # 网络配置networks:  app-network:    driver: bridge # 卷配置volumes:  db_data:    driver: local  redis_data:    driver: localKubernetes 部署方案Deployment123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122apiVersion: apps/v1kind: Deploymentmetadata:  name: flask-app  namespace: default  labels:    app: flask-app    tier: backendspec:  replicas: 3  selector:    matchLabels:      app: flask-app  template:    metadata:      labels:        app: flask-app        tier: backend    spec:      securityContext:        runAsNonRoot: true      # 禁止 root 运行        runAsUser: 1000         # 使用非 root 用户        runAsGroup: 1000        fsGroup: 1000        seccompProfile:          type: RuntimeDefault  # 启用 seccomp 安全策略      containers:      - name: flask-app        image: myregistry.com/myflaskapp:1.0.0        imagePullPolicy: IfNotPresent  # 生产环境建议用 Always        ports:        - name: http          containerPort: 8000          protocol: TCP        env:        - name: FLASK_ENV          value: "production"        - name: DATABASE_URL          valueFrom:            secretKeyRef:              name: flask-app-secrets              key: database-url        - name: REDIS_URL          valueFrom:            secretKeyRef:              name: flask-app-secrets              key: redis-url        - name: SECRET_KEY          valueFrom:            secretKeyRef:              name: flask-app-secrets              key: secret-key        resources:          requests:            memory: "256Mi"            cpu: "100m"          limits:            memory: "512Mi"   # 超过会 OOM Kill            cpu: "500m"        livenessProbe:          httpGet:            path: /health            port: 8000            scheme: HTTP          initialDelaySeconds: 30  # 启动后 30 秒开始检查          periodSeconds: 10        # 每 10 秒检查一次          timeoutSeconds: 3        # 超时 3 秒          successThreshold: 1          failureThreshold: 3      # 失败 3 次后重启容器        readinessProbe:          httpGet:            path: /health            port: 8000            scheme: HTTP          initialDelaySeconds: 10  # 启动后 10 秒开始检查          periodSeconds: 5         # 每 5 秒检查一次          timeoutSeconds: 2          successThreshold: 1          failureThreshold: 3      # 失败 3 次后从 Service 移除        startupProbe:          httpGet:            path: /health            port: 8000            scheme: HTTP          failureThreshold: 30     # 最多重试 30 次          periodSeconds: 5         # 每 5 秒一次,共 150 秒容忍慢启动          timeoutSeconds: 3        securityContext:          allowPrivilegeEscalation: false  # 禁止提权          readOnlyRootFilesystem: true     # 根文件系统只读          capabilities:            drop:            - ALL                          # 删除所有 Linux capabilities          privileged: false        volumeMounts:        - name: tmp-volume          mountPath: /tmp        - name: config-volume          mountPath: /app/config          readOnly: true      imagePullSecrets:      - name: registry-secret  # 如果使用私有镜像仓库      affinity:        podAntiAffinity:          preferredDuringSchedulingIgnoredDuringExecution:          - weight: 100            podAffinityTerm:              labelSelector:                matchExpressions:                - key: app                  operator: In                  values:                  - flask-app              topologyKey: kubernetes.io/hostname  # 避免所有 Pod 调度到同一节点      volumes:      - name: tmp-volume        emptyDir:          medium: Memory  # 使用内存卷,更快          sizeLimit: 100Mi      - name: config-volume        configMap:          name: flask-app-configService1234567891011121314151617apiVersion: v1kind: Servicemetadata:  name: flask-app-service  namespace: default  labels:    app: flask-app    tier: backendspec:  type: ClusterIP  selector:    app: flask-app  ports:  - name: http    port: 80        # Service 端口    targetPort: 8000  # Pod 端口    protocol: TCPingress-nginx12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879apiVersion: networking.k8s.io/v1kind: Ingressmetadata:  name: flask-app-ingress  namespace: default  annotations:    # ==================== Nginx 配置 ====================    kubernetes.io/ingress.class: "nginx"         # 启用 HTTPS 重定向    nginx.ingress.kubernetes.io/ssl-redirect: "true"    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"         # 限流(每秒 10 个请求,突发 20)    nginx.ingress.kubernetes.io/limit-rps: "10"    nginx.ingress.kubernetes.io/limit-burst-multiplier: "2"         # 客户端真实 IP    nginx.ingress.kubernetes.io/enable-real-ip: "true"    nginx.ingress.kubernetes.io/proxy-real-ip-cidr: "0.0.0.0/0"         # 连接超时    nginx.ingress.kubernetes.io/proxy-connect-timeout: "60"    nginx.ingress.kubernetes.io/proxy-send-timeout: "60"    nginx.ingress.kubernetes.io/proxy-read-timeout: "60"         # 缓冲区大小    nginx.ingress.kubernetes.io/proxy-buffering: "on"    nginx.ingress.kubernetes.io/proxy-buffer-size: "16k"    nginx.ingress.kubernetes.io/proxy-buffers-number: "4"         # Gzip 压缩    nginx.ingress.kubernetes.io/enable-gzip: "true"    nginx.ingress.kubernetes.io/gzip-level: "6"    nginx.ingress.kubernetes.io/gzip-min-length: "1024"    nginx.ingress.kubernetes.io/gzip-types: "text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript"         # 安全头    nginx.ingress.kubernetes.io/configuration-snippet: |      add_header X-Frame-Options "SAMEORIGIN" always;      add_header X-Content-Type-Options "nosniff" always;      add_header X-XSS-Protection "1; mode=block" always;      add_header Referrer-Policy "strict-origin-when-cross-origin" always;         # 认证    # nginx.ingress.kubernetes.io/auth-type: basic    # nginx.ingress.kubernetes.io/auth-secret: flask-app-basic-auth    # nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"         # 自定义错误页面    # nginx.ingress.kubernetes.io/custom-http-errors: "404,500,502,503,504"    # nginx.ingress.kubernetes.io/default-backend: custom-error-pages         # 重写目标    # nginx.ingress.kubernetes.io/rewrite-target: /$1         # WAF(如果安装了 ModSecurity)    # nginx.ingress.kubernetes.io/enable-modsecurity: "true"    # nginx.ingress.kubernetes.io/modsecurity-snippet: |    #   SecRuleEngine On    #   SecRequestBodyAccess On spec:  tls:  - hosts:    - flask.example.com    secretName: flask-app-tls-secret  # TLS 证书 Secret   rules:  - host: flask.example.com    http:      paths:      - path: /        pathType: Prefix        backend:          service:            name: flask-app-service            port:              number: 80
  • [问题求助] 请问L实例上websocket服务协议是不是默认关闭的?
    前端通过const ws = new WebSocket('ws://websocket'); 访问后端,返回失败,直接进入onerrorusted: truebubbles: falsecancelBubble: falsecancelable: falsecomposed: falsecurrentTarget: WebSocket {url: 'ws://websocket', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}defaultPrevented: falseeventPhase: 0returnValue: truesrcElement: WebSocket {url: 'ws://websocket', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}target: WebSocket {url: 'ws://websocket', readyState: 3, bufferedAmount: 0, onopen: ƒ, onerror: ƒ, …}timeStamp: 203.5type: "error"
  • [问题求助] 请教前端fetch后端报ERR_CONNECTION_REFUSED,但在服务器直接curl后端api成功
    会是什么问题导致的?会不会是安全组设置错误导致的?非常感谢!
  • [互动交流] Flask能否返回一个html带上一个参数呢?
    @app.route('/uploader',methods=['GET','POST'])def uploader(): if request.method == 'POST': f = request.files['file'] print(request.files) f.save(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(f.filename))) return 'file uploaded successfully' else: return render_template('upload.html')像这样的Flask代码,它返回一个字符串或者返回一个html(都会打开一个新页面)那我如何返回html的同时,也带一个字符串参数呢?然后在新的html页面里,如何在新的请求中加上这个字符串参数呢?感谢!
  • [技术干货] Python的flask常用函数route()【转】
    目录一、route()路由概述二、静态路由和动态路径方式1:静态路由方式2:动态路由三、route()其它参数1.methods=[‘GET’,‘POST’]一、route()路由概述功能:将URL绑定到函数路由函数route()的调用有两种方式:静态路由和动态路由二、静态路由和动态路径方式1:静态路由@app.route(“/xxx”) xxx为静态路径 如::/index / /base等,可以返回一个值、字符串、页面等12345678910111213from flask import Flaskapp = Flask(__name__) @app.route('/hello')def hello_world():    return 'Hello World!!!'     @app.route('/pro')def index():    return render_template('login.html') if __name__ == '__main__':    app.run(debug = True)方式2:动态路由采用<>进行动态url的传递@app.route(“/”),这里xxx为不确定的路径。123456789from flask import Flaskapp = Flask(__name__) @app.route('/hello/<name>')def hello_name(name):   return 'Hello %s!' % name if __name__ == '__main__':   app.run(debug = True)如果浏览器地址栏输入:http:// localhost:5000/hello/w3cschool则会在页面显示:Hello w3cschool!三、route()其它参数1.methods=[‘GET’,‘POST’]当前视图函数支持的请求方式,不设置默认为GET请求方式不区分大小写methods=[‘GET’] 支持的请求方法为GETmethods=[‘POST’] 支持的请求方法为POSTmethods=[‘GET’,‘POST’] 支持的请求方法为POST GET123456789101112@app.route('/login', methods=['GET', 'POST'])  # 请求参数设置不区分大小写,源码中自动进行了upperdef login():    if request.method == 'GET':        return render_template('login.html')    elif request.method == 'POST':        username = request.form.get('username')        pwd = request.form.get('pwd')        if username == 'yang' and pwd == '123456':            session['username'] = username            return 'login successed 200  ok!'        else:            return 'login failed!!!'
  • [技术干货] Python使用Flask Migrate模块迁移数据库【转】
    前言安装初始化项目结构三大命令前言本篇文章,阐述一下Flask中数据库的迁移为什么要说数据库迁移呢?比如我们以前有一个数据库,里面的信息有 id, name现在我想再加一个gender属性,应该怎么办呢?不可能直接把数据库删除掉吧。然后重新创建一个,因此本文介绍一种,通过数据库迁移的方法,可以保留原始的数据,并完成添加新的一列的方法。安装直接使用pip install flask-migrate即可初始化项目结构123# exts.pyfrom flask_sqlalchemy import SQLAlchemydb = SQLAlchemy()老样子,我们还是在exts.py中,初始我们的数据库,这里时放Flask扩展的地方。1234567# models.pyfrom exts import dbclass User(db.Model):    id = db.Column(db.Integer,primary_key=True)    username = db.Column(db.String(80),unique=True)    def __repr__(self):        return '<User %s>' % self.usernamemodels.py是放模型的地方。1234567891011# app.pyfrom flask import Flaskfrom exts import dbfrom flask_migrate import Migrateapp = Flask(__name__)app.config['SQLALCHEMY_DATABASE_URI'] = "sqlite:///foo.db"# !!!绑定app和数据库db.init_app(app)migrate = Migrate(app,db)if __name__ == '__main__':    app.run()主程序,我们在!!!进行初始化数据库迁移的migrate 对象。初始化之后我们可以执行下面的三大命令去创建我们的数据库。就不需要db.create_all()命令啦。三大命令# (1)初始化flask db init# 把当前的模型添加到迁移文件flask db migrate# 将映射文件真正的映射到数据库中flask db upgrade(1)新建一个名字为migrations的文件夹,并且记录一个数据库版本号其他问题:如果报错[flask_migrate] Error: Can‘t locate revision identified by '409392ed6301'答:db revision --rev-id 409392ed6301 409392ed6301是问题的版本号Error: While importing ‘app’, an ImportError was raised.答:请先去运行一下app.py,确保app.py可以运行Error: Could not locate a Flask application. You did not provide the “FLASK_APP” environment variable, and a “wsgi.py” or “app.py” module was not found in the current directory. 答:请确保执行命令时的文件夹中有app.py文件
  • [技术干货] Python Flask中Cookie和Session区别详解【转】
    目录前言安装创建虚拟环境进入虚拟环境安装 flaskCookie的使用Session的使用前言本篇文章,阐述一下Flask中Cookie和Session为什么要说Cookie和Session呢?答:因为http请求是无状态的,怎么理解呢?当你访问B站时,如果你没有Cookie或者Session,B站就认为你是一个没有登录的用户。如果你有Cookie或Session,那么B站就知道你登录了,并且知道你是谁。所以可以把跟你相关的资料返回给你两者的区别:答:Cookie是明文,Session是加密的Cookie。安装创建虚拟环境123mkdir myprojectcd myprojectpython3 -m venv venv进入虚拟环境1. venv/bin/activate安装 flask1pip install FlaskCookie的使用12345678910111213141516from flask import Flask, Response, requestapp = Flask(__name__)@app.route("/set_cookie")def set_cookie():    response = Response("set cookie success")    response.set_cookie("user_id","123")    return response@app.route("/get_cookie")def get_cookie():    cookies = request.cookies    if cookies.get("user_id") == "123":        return "尊敬的用户你好,你的cookies是" + str(cookies)    else:        return "你不是VIP,请先获取身份"if __name__ == "__main__":    app.run(debug=True,port=5000)第一次首先访问/home地址,会发现你没有Cookie给你返回一个你不是VIP,请先获取身份但是当我们首先访问/set_cookie,我们就会获得一个cookie,然后我们在访问/home地址,就可以成功的显示尊敬的用户您好了Session的使用12345678910111213141516from flask import Flask, sessionapp = Flask(__name__)# !!!你需要设置一个SECRET_KEY用来加密Cookieapp.config["SECRET_KEY"] = "123"@app.route("/set_session")def set_session():    session["user_name"] = "zhuxiaoxiao"    return "设置session成功" + session["user_name"]@app.route("/home")def home():    if session.get("user_name") == "zhuxiaoxiao":        return "尊敬的用户,登录成功"    else:        return "请先登录好吗?"if __name__ == "__main__":    app.run(debug=True,port=5000)Session的使用方式整体和Cookie是一样的。需要注意的是使用Session前,必须要进行设置SECRET_KEY,用来加密Cookie
  • [技术干货] 首发】flask 实现ajax 数据入库,并掌握文件上传[转载]
    lask 实现ajax 数据入库在正式编写前需要了解一下如何在 python 函数中去判断,一个请求是 get 还是 post。python 文件代码如此所示:# route()方法用于设定路由; @app.route('/hello.html', methods=['GET', 'POST']) def hello_world(): if request.method == 'GET': # args = request.args return render_template('hello.html') if request.method == "POST": print("POST请求")上述代码通过 requests.method 属性判断当前请求类型,然后实现相应的逻辑。注意上述内容中的 @app.route('/hello.html', methods=['GET', 'POST']) ,绑定的方法由参数 methods 决定。HTML 页面代码如下所示:<!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset="UTF-8"> <title>这是第一个HTML页面</title> <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script> </head> <body> {{name}} <input type="button" value="点击发送请求" id="btn" /> <script> $(function() { $('#btn').on('click', function() { alert($(this).val()); }); }) </script> </body> </html>在 HTML 页面中提前导入 jquery 的 CDN 配置,便于后续实现模拟请求操作。再次完善一些 POST 请求的相关参数判断,通过 requests.form 获取表单参数。# route()方法用于设定路由; @app.route('/hello.html', methods=['GET', 'POST']) def hello_world(): if request.method == 'GET': args = request.args name = args.get('name') return render_template('hello.html',name=name) if request.method == "POST": print("POST请求") arges = request.form print(arges) return "PPP"同步修改一下前端请求部分,这里改造主要需要的是前端知识。<body> {{name}} <input type="button" value="点击发送请求" id="btn" /> <script> $(function() { $('#btn').on('click', function() { //alert($(this).val()); $.post('./hello.html', function(result) { console.log(result); }) }); }) </script> </body>测试的时候同步开启浏览器的开发者工具,并且切换到网络请求视图,查看请求与返回数据。数据传递到后台之后,只需要使用上一篇博客涉及的内容即可将数据存储到 MySQL 中。实现文件上传了解了POST请求之后,就可以通过该模式实现文件上传操作了。优先修改 HTML 页面,实现一个文件选择按钮。<input type="file" id="file" /> <script type="text/javascript"> $(function() { $('#btn').on('click', function() { //alert($(this).val()); $.post('./hello.html', function(result) { console.log(result); }) }); var get_file = document.getElementById("file"); get_file.onchange = function(e) { file = e.currentTarget.files[0]; //所有文件,返回一个数组 var form_data = new FormData(); form_data.append("file", file); console.log(form_data); form_data.append("file_name", e.currentTarget.files[0].name); $.ajax({ url: '/upload', type: 'post', data: form_data, contentType: false, processData: false, success: function(res) { console.log(res.data); } }); } }) </script>服务端处理文件的代码如下所示@app.route('/upload', methods=['POST'], strict_slashes=False) def upload(): if request.method == "POST": print("POST请求") file = request.files.get('file') name = request.form.get('file_name') print(name) file.save("./"+name) # print(name) return "PPP"这里需要注意的是如果 Python 端存在BUG,前端的AJAX请求会出现 400或者500错误。文件名通过前端传递 file_name 参数获取。本案例可以扩展上传成功之后,返回JSON数据到前端进行后续处理。项目在实测的过程中发现一个问题,在读取前台传递的文件流时,需要使用 request.files.get() 方法实现,而不能用 request.form['参数名'] 。原文链接:https://bbs.huaweicloud.com/blogs/334929
  • [技术干货] python+flask编写一个简单的登录接口
    在学习接口测试的时候往往会因为没有实际操作的接口进行测试而烦恼,这里教大家自己编写两个接口用于学习接口测试1、编写一个登录的接口2、在pycharm运行3、使用apipost进行登录接口测试输入url和参数值进行访问,访问成功。4、在pycharm查看是否正常进行访问5、在编写一个需要登录返回的token直接访问的查询接口6、运行登录和查询两个接口7、使用apipost进行登录和查询的接口测试首先进行登录的接口测试获取返回的token使用登录返回的token值进行查询的接口测试8、使用的工具接口编写工具:python、pycharm、flask框架接口测试工具:apipost接口测试工具下载地址:https://www.apipost.cn
总条数:16 到第
上滑加载中