首页 > 解决方案 > 为什么在我的 Flask 应用程序中调用 csrf_token() 会引发“无法将元组连接到字节”错误?

问题描述

我正在尝试在fetch()调用中包含 X-CSRFToken 标头,并且根据 Flask-WTF文档,从您的模板调用 csrf_token() 将在您的代码中返回令牌。但是,当我拨打电话时,我的应用程序会引发错误csrf_token()

错误状态:“无法将元组连接到字节。” 回溯包括在下面。这是模板中的 Javascript,通过render_template()

    <script>
        const form = document.querySelector("form");

        form.addEventListener("submit", function(e){
            e.preventDefault();

            let opts = {
                method: this.method.value,
                headers: new Headers({
                    "X-CSRFToken": "{{ csrf_token() }}"
                })
            };

            if(this.method.value!="GET"){
                opts.body = this.data.value;
            }

            fetch( this.api_endpoint.value, opts );
        });
    </script>

下面是路由调用的 .py 文件render_template()

from flask import Blueprint, render_template

bp = Blueprint('main', __name__,
    template_folder="templates",
    static_folder="static")

@bp.route("/")
def index():
   return render_template("home.html")

@bp.route("/apitester")
def apitester():
    return render_template("apitester.htm")

这是init .py 文件,其中appcsrf对象被实例化

from flask import Flask, render_template
from flask_wtf.csrf import CSRFProtect, CSRFError
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

csrf = CSRFProtect()

def create_app():
    app = Flask(__name__, instance_relative_config=False)

    app.config.from_object('application.appsettings')

    db.init_app(app)

    csrf.init_app(app)

    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404err.htm', reason=e.description), 404

    @app.errorhandler(CSRFError)
    def handle_csrf_error(e):
        return render_template('400err.htm', reason=e.description), 400

    with app.app_context():
        from exercises.bp import bp as bp_exercise
        from main.bp import bp as bp_main
        from application.models import Exercise

        app.register_blueprint(bp_main, url_prefix="/")
        app.register_blueprint(bp_exercise, url_prefix="/exercises")

        return app

这是回溯:

Traceback (most recent call last):
  File "C:\pyeip\venv\Lib\site-packages\flask_wtf\csrf.py", line 49, in generate_csrf
    token = s.dumps(session[field_name])
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\serializer.py", line 167, in dumps
    rv = self.make_signer(salt).sign(payload)
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\timed.py", line 42, in sign
    return value + sep + self.get_signature(value)
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\signer.py", line 143, in get_signature
    key = self.derive_key()
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\signer.py", line 130, in derive_key
    return self.digest_method(salt + b"signer" + self.secret_key).digest()
TypeError: can't concat tuple to bytes

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 2464, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 2450, in wsgi_app
    response = self.handle_exception(e)
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 1867, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\pyeip\venv\Lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\pyeip\venv\Lib\site-packages\flask\_compat.py", line 39, in reraise
    raise value
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\pyeip\venv\Lib\site-packages\flask\app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\pyeip\main\bp.py", line 14, in apitester
    return render_template("apitester.htm")
  File "C:\pyeip\venv\Lib\site-packages\flask\templating.py", line 137, in render_template
    return _render(
  File "C:\pyeip\venv\Lib\site-packages\flask\templating.py", line 120, in _render
    rv = template.render(context)
  File "C:\pyeip\venv\Lib\site-packages\jinja2\environment.py", line 1090, in render
    self.environment.handle_exception()
  File "C:\pyeip\venv\Lib\site-packages\jinja2\environment.py", line 832, in handle_exception
    reraise(*rewrite_traceback_stack(source=source))
  File "C:\pyeip\venv\Lib\site-packages\jinja2\_compat.py", line 28, in reraise
    raise value.with_traceback(tb)
  File "C:\pyeip\main\templates\apitester.htm", line 36, in top-level template code
    const token = {{ csrf_token() }};
  File "C:\pyeip\venv\Lib\site-packages\flask_wtf\csrf.py", line 52, in generate_csrf
    token = s.dumps(session[field_name])
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\serializer.py", line 167, in dumps
    rv = self.make_signer(salt).sign(payload)
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\timed.py", line 42, in sign
    return value + sep + self.get_signature(value)
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\signer.py", line 143, in get_signature
    key = self.derive_key()
  File "C:\pyeip\venv\Lib\site-packages\itsdangerous\signer.py", line 130, in derive_key
    return self.digest_method(salt + b"signer" + self.secret_key).digest()
TypeError: can't concat tuple to bytes

标签: pythonflaskflask-wtformscsrf-token

解决方案


从阅读回溯的最后部分

return self.digest_method(salt + b"signer" + >self.secret_key<).digest()

检查 Flask 应用程序配置中的 SECRET_KEY 值(或app.secret_key),在密码字符串后查找额外的逗号。

例如:

SECRET_KEY = "very secret string", # extraneous comma, it's tuple

推荐阅读