Gunicorn 未加载 Flask 应用密钥

问题描述

当我尝试使用 gunicorn -w 3 wsgi:app 运行 Flask 并通过 IP 地址访问页面时,我收到以下错误RuntimeError: A secret key is required to use CSRF. 在终端中。

我不确定是什么导致了这个问题,因为:我在 .env 中设置了一个密钥并且我确认它正在被 Flask 加载,因为当我使用 flask run --host=0.0.0.0 运行应用程序时,然后代码print(app.config['SECRET_KEY']) 将密钥打印到终端。
我想这与 Gunicorn 需要不同的代码来加载密钥有关。

我的 Flask 应用的结构如下:

-denise
--configmodule.py
--__init__.py
--site.db
--models.py
--main
--static
--template
-migrations
-venv
-wsgi.py
-.env

我没有太多设置服务器的经验,以下是相关文件中的代码

__init__.py 文件

from flask import Flask
from flask_sqlalchemy import sqlAlchemy
from denise.configmodule import Config
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_mail import Mail
from flask_migrate import Migrate
from flask_wtf.csrf import CSRFProtect

db = sqlAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
mail = Mail()
csrf = CSRFProtect()

def create_app():
    app = Flask(__name__)
    app.config.from_object(Config)
    csrf.init_app(app)
    migrate = Migrate(app,db)
    db.init_app(app)
    bcrypt.init_app(app)
    login_manager.init_app(app)
    mail.init_app(app)
    print('SECRET_KEY')
    print(app.config['SECRET_KEY'])
    print('SECRET_KEY')

    #with app.app_context()
    from .main.routes import main

    app.register_blueprint(main)
    return app                                

configmodule.py 文件

from os import environ,path
from dotenv import load_dotenv

basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir,'.env'))

class Config(object):
    DEBUG=True
    SECRET_KEY=environ.get('SECRET_KEY')
    sqlALCHEMY_DATABASE_URI='sqlite:///site.db'
    ENV='development'
    MAIL_SERVER = 'smtp.googlemail.com'
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = environ.get('EMAIL_USER')
    MAIL_PASSWORD = environ.get('EMAIL_PASS')
    MAIL_DEFAULT_SENDER = environ.get('EMAIL_DEFAULT_SENDER')
    MAIL_MAX_EMAILS = 5
    RECAPTCHA_PUBLIC_KEY = environ.get('RECAPTCHA_PUBLIC')
    RECAPTCHA_PRIVATE_KEY = environ.get('RECAPTCHA_PRIVATE')                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       

Wsgi.py 文件


from denise import create_app
from dotenv import load_dotenv

load_dotenv('.env')

app = create_app()

if __name__ == "__main__":
    app.run()

.env 文件

SECRET_KEY='hiiii17011b97b7ed4aeb9ae7f75a0b66a006c8efd4ab0759e5d2'
EMAIL_USER='email@email.email'
EMAIL_DEFAULT_SENDER='email@email.email'
EMAIL_PASS='password'
RECAPTCHA_PUBLIC='randomstringofletter'
RECAPTCHA_PRIVATE='lalalalalal'                                                                  

我尝试了 Corey Schafer 和 Pretty Printed 的一些关于将 Flask 应用程序部署到生产环境的教程。我想我在某处错过了一些东西。

解决方法

(用手机写的)

看看你的 wsgi 文件:

  1. 首先导入 create_app,它会转到您的 __init__ 文件
  2. __init__ 文件导入 Config,它不是延迟初始化,而是在导入时(我会更改此设置,但这不是问题)
  3. 在配置文件中取__file__的目录名,即denise,然后用.env加入它,而denise目录中没有env文件

关于运行flask run调试,我不使用flask,但它可能只是不使用wsgi文件。

,

很好的旧垃圾邮件打印语句来救援。

注意,我使用 gunicorn -w 1 -t 1 wsgi:app 来确保单个工作线程和线程,否则控制台会收到来自多个来源的 print 语句的垃圾邮件。

导入和加载订单可能是罪魁祸首。 load_dotenv(path.join(basedir,'.env')) 中的语句 configmodule.py 也存在问题。它在目录 .env 中查找 denise 文件并无提示地失败。

我制作了一个可重现的例子,见下文。

解决方法是:

  • 在从 load_dotenv('.env') 导入任何内容之前先在 wsgi.py 中执行 denise
  • 将您在 configmodule.py 中的加载方式更改为 load_dotenv(path.join(basedir,'../.env')),但由于您使用了应用程序工厂模式结构,我不确定这是否可取。这可能会导致未来出现不良行为。

configmodule.py

​​>
from os import environ,path
from dotenv import load_dotenv,dotenv_values
from pprint import pprint


print('--------------------------------------------------------------------------------------------------------------------------------')
print('configmodule')
print('--------------------------------------------------------------------------------------------------------------------------------')


basedir = path.abspath(path.dirname(__file__))
load_dotenv(path.join(basedir,'.env'))
try:
    with open(path.join(basedir,'.env')) as infile:
        for line in infile:
            print(line)
except:
    print('=========================== FILENOTFOUND')


class Config(object):
    print('--------------------------------------------------------------------------------------------------------------------------------')
    print('configmodule:Config')
    print('--------------------------------------------------------------------------------------------------------------------------------')
    pprint(dotenv_values(path.join(basedir,'.env')))
    print('--------------------------------------------------------------------------------------------------------------------------------')
    pprint(environ)
    DEBUG=True
    SECRET_KEY=environ.get('SECRET_KEY')
    SQLALCHEMY_DATABASE_URI='sqlite:///site.db'
    ENV='development'
    MAIL_SERVER = 'smtp.googlemail.com'
    MAIL_PORT = 587
    MAIL_USE_TLS = True
    MAIL_USERNAME = environ.get('EMAIL_USER')
    MAIL_PASSWORD = environ.get('EMAIL_PASS')
    MAIL_DEFAULT_SENDER = environ.get('EMAIL_DEFAULT_SENDER')
    MAIL_MAX_EMAILS = 5
    RECAPTCHA_PUBLIC_KEY = environ.get('RECAPTCHA_PUBLIC')
    RECAPTCHA_PRIVATE_KEY = environ.get('RECAPTCHA_PRIVATE')

wsgi.py

​​>
from os import environ
from denise import create_app
from dotenv import load_dotenv,dotenv_values


print('--------------------------------------------------------------------------------------------------------------------------------')
print('WSGI')
print('--------------------------------------------------------------------------------------------------------------------------------')
load_dotenv('.env')
print(dotenv_values('.env'))
print('--------------------------------------------------------------------------------------------------------------------------------')
with open('.env') as infile:
    print(infile.readlines())
print('--------------------------------------------------------------------------------------------------------------------------------')
print(environ)
print('--------------------------------------------------------------------------------------------------------------------------------')

app = create_app()

if __name__ == "__main__":
    app.run()

我建议修改 wsgi.py 以防止未来出现问题,如上所述,尽管这可能违反 PEP8 标准。

固定的 wsgi.py

​​>
from dotenv import load_dotenv
load_dotenv('.env')

from denise import create_app


app = create_app()

if __name__ == "__main__":
    app.run()