Python Flask: werkzeug.exceptions.BadRequestKeyError

Oct 08 2020

Tôi mới sử dụng Python Flask và tôi đang cố gắng thiết kế blog cá nhân của mình. Vì nó chỉ dành cho mục đích sử dụng riêng tư, tôi muốn đặt trang "thêm" cho bài đăng dưới dạng khu vực được bảo vệ với tên người dùng và mật khẩu chỉ để thêm và quản lý các bài viết của tôi. Để không hiển thị thông tin đăng nhập trong mã nguồn, tôi quyết định lưu trữ trong tệp .yaml và truy xuất chúng ( {"Franz":{"pw":"Biberkopf"}}) thông qua đăng nhập hàm.

Nhưng khi tôi đặt "Franz" làm tên người dùng và "Biberkopf" làm mật khẩu, tôi gặp lỗi này:

werkzeug.exceptions.BadRequestKeyError

werkzeug.exceptions.BadRequestKeyError: 400 Yêu cầu sai: Trình duyệt (hoặc proxy) đã gửi một yêu cầu mà máy chủ này không thể hiểu được.
KeyError: 'pw'

Traceback (cuộc gọi gần đây nhất cuối cùng)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 2464, trong cuộc gọi

return self.wsgi_app (môi trường, start_response)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 2450, trong wsgi_app

response = self.handle_exception (e)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 1867, trong handle_exception

reraise (exc_type, exc_value, tb)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask_compat.py", dòng 39, được sắp xếp lại

nâng cao giá trị

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 2447, trong wsgi_app

response = self.full_dispatch_request ()

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 1952, trong full_dispatch_request

rv = self.handle_user_exception (e)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 1821, trong handle_user_exception

reraise (exc_type, exc_value, tb)

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask_compat.py", dòng 39, được sắp xếp lại

nâng cao giá trị

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-package \ flask \ app.py", dòng 1950, trong full_dispatch_request

rv = self.dispatch_request ()

Tệp "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-pack \ flask \ app.py", dòng 1936, trong Dispatch_request

trả về self.view_functionsrule.endpoint

Tệp "C: \ Users \ franc \ OneDrive \ Desktop \ Blog_Project \ blog_prova.py", dòng 102, khi đăng nhập

user.id = username
flask_login.login_user (người dùng)
trả lại chuyển hướng (url_for ("thêm"))
else:
error = "Leider haben Sie die falschen Zugangsdaten."
return render_template ("login.html", error = error)
trả về render_template ("login.html")

Mã:

@app.route('/add')

@login_required

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\templating.py", line 136, in render_template

ctx.app.update_template_context(context)

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask\app.py", line 838, in update_template_context

context.update(func())

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_login\utils.py", line 379, in _user_context_processor

return dict(current_user=_get_user())

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_login\utils.py", line 346, in _get_user

current_app.login_manager._load_user()

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_login\login_manager.py", line 331, in _load_user

user = self._load_user_from_request(request)

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\flask_login\login_manager.py", line 390, in _load_user_from_request

user = self._request_callback(request)

File "C:\Users\franc\OneDrive\Desktop\Blog_Project\blog_prova.py", line 85, in request_loader

    username = request.form.get("username")  # comunica col template <input ...name = "username"> Username </input>

    if username not in users:

        return

    user = User()

    user.id = username

    user.is_authenticated = request.form["pw"] == users[username]["pw"]

 

    return user

 

 

@app.route('/login', methods=["POST", "GET"])

File "C:\Users\franc\AppData\Local\Programs\Python\Python38-32\Lib\site-packages\werkzeug\datastructures.py", line 442, in __getitem__

raise exceptions.BadRequestKeyError(key)

werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'pw'

Trình gỡ lỗi đã gặp một ngoại lệ trong ứng dụng WSGI của bạn. Bây giờ bạn có thể xem lại dấu vết đã dẫn đến lỗi.

Để chuyển đổi giữa bản truy nguyên tương tác và bản rõ, bạn có thể nhấp vào dòng tiêu đề "Traceback". Từ bản ghi lại văn bản, bạn cũng có thể tạo một bản dán của nó. Để thực thi mã, hãy di chuột qua khung bạn muốn gỡ lỗi và nhấp vào biểu tượng bảng điều khiển ở phía bên phải.

Bạn có thể thực thi mã Python tùy ý trong các khung ngăn xếp và có một số trình trợ giúp bổ sung có sẵn để xem xét nội dung:

dump() shows all variables in the frame
dump(obj) dumps all that's known about the object

Edit: If I run the Pycharm debugger, it seems that there are some problems with the method is_authenticated (Property 'is_authenticated' cannot be set). I don´t know if it is somehow inherent.

This is my sample code: 


```
from datetime import datetime
import flask_login
import yaml
from flask import Flask, render_template, request, redirect, url_for 
from flask_login import LoginManager, UserMixin, login_required, logout_user
from flask_sqlalchemy import SQLAlchemy


app = Flask(__name__)
app.config['TESTING'] = False
app.config['SECRET_KEY'] = 'chiavesegreta'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////Users/Desktop/Blog_Project/blog2.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)

login_manager = LoginManager()
login_manager.init_app(app)


# {"Franz":{"pw":"Biberkopf"}}
with open(r'C://Users//Desktop//application.yaml') as file:
    users = yaml.full_load(file)


class User(UserMixin):
    pass


class Blogpost2(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(50))
    subtitle = db.Column(db.String(50))
    author = db.Column(db.String(20))
    date_posted = db.Column(db.DateTime)
    content = db.Column(db.Text)

db.create_all()


@app.route('/')
def index():
    posts = Blogpost2.query.order_by(Blogpost2.date_posted.desc()).all()

    return render_template('index.html', posts=posts)


@app.route('/about')
def about():
    return render_template('about.html')


@app.route('/post/<int:post_id>')
def post(post_id):
    post = Blogpost2.query.filter_by(id=post_id).one() 

    date_posted = post.date_posted.strftime('%d %B, %Y')

    return render_template('post.html', post=post, date_posted=date_posted)


@login_manager.user_loader
def user_loader(username):
    if username not in users:
        return

    user = User()
    user.id = username
    return user


@login_manager.request_loader
def request_loader(request):
    username = request.form.get("username")  
    if username not in users:
        return
    user = User()
    user.id = username
    user.is_authenticated = request.form["pw"] == users[username]["pw"]

    return user


@app.route('/login', methods=["POST", "GET"])
def login():
    error = None
    if request.method == 'POST':
        username = request.form.get('username')
        if request.form.get('pw') == users[username]["pw"]:
            user = User()
            user.id = username
            flask_login.login_user(user)
            return redirect(url_for("add"))
        else:
            error = "Leider haben Sie die falschen Zugangsdaten."
            return render_template("login.html", error=error)
    return render_template("login.html")


@app.route('/add')
@login_required
def add():
    return render_template('add.html')


@app.route('/logout')
def logout():
    logout_user()
    return "Logged out !"



@app.route('/contact')
def contact():
    return render_template('contact.html')


@app.route('/prova')
def prova():
    return render_template('prova.html')


@app.route('/addpost', methods=['POST'])
def addpost():
    title = request.form['title']
    subtitle = request.form['subtitle']
    author = request.form["author"]
    content = request.form['content']

    post = Blogpost2(title=title, subtitle=subtitle, author=author, content=content, date_posted=datetime.now())

    db.session.add(post)
    db.session.commit()

    return redirect(url_for('index'))


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


Ý tưởng sẽ là, nếu ai đó cố gắng đăng nhập mà không có tên người dùng và mật khẩu phù hợp, một thông báo lỗi sẽ được hiển thị. Với thông tin đăng nhập phù hợp, bạn có thể có quyền truy cập vào trang "thêm". Đây là trang login.html của tôi:

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">

  <title>Clean Blog - Start Bootstrap Theme</title>

  <!-- Bootstrap core CSS -->
  <!-- <link href="vendor/bootstrap/css/bootstrap.min.css" rel="stylesheet"> -->
  <link href= "{{ url_for('static', filename='bootstrap.min.css')}}" rel="stylesheet">

  <!-- Custom fonts for this template -->
  <!--  <link href="vendor/fontawesome-free/css/all.min.css" rel="stylesheet" type="text/css">                              -->
  <link href="{{url_for('static', filename = 'fontawesome.min.css')}}" rel="stylesheet" type="text/css">
  <link href='https://fonts.googleapis.com/css?family=Lora:400,700,400italic,700italic' rel='stylesheet' type='text/css'>
  <link href='https://fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,800italic,400,300,600,700,800' rel='stylesheet' type='text/css'>

  <!-- Custom styles for this template -->
  <link href="{{url_for('static', filename = 'clean-blog.min.css')}}" rel="stylesheet">

</head>

<body>

   <!-- Navigation -->
  <nav class="navbar navbar-expand-lg navbar-light fixed-top" id="mainNav">
    <div class="container">
      <a class="navbar-brand" href= "{{ url_for('index') }}">Start Bootstrap</a>
      <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
        Menu
        <i class="fas fa-bars"></i>
      </button>
      <div class="collapse navbar-collapse" id="navbarResponsive">
        <ul class="navbar-nav ml-auto">
          <li class="nav-item">
            <a class="nav-link" href="{{ url_for('index')}}">Home</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="{{ url_for('about')}}">About</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="{{ url_for('contact')}}">Contact</a>
          </li>
          <li class="nav-item">
            <a class="nav-link" href="{{ url_for('prova')}}">Prova</a>
          </li>
        </ul>
      </div>
    </div>
  </nav>

  <!-- Page Header -->
  <header class="masthead" style="background-image: url('img/contact-bg.jpg')">
    <div class="overlay"></div>
    <div class="container">
      <div class="row">
        <div class="col-lg-8 col-md-10 mx-auto">
          <div class="page-heading">
            <h1>Login</h1>
          </div>
        </div>
      </div>
    </div>
  </header>



  <hr>

  <!-- Footer -->
  <footer><!-- Main Content -->
  <div class="container"> <!-- crea contenitore x la larghezza del layout -->
    <div class="row"> <!--  crea riga  -->
      <div class="col-lg-8 col-md-10 mx-auto"> <!-- crea colonna -->
        <p>Login zu dem privaten Area dieses Blogs</p>

        <form action = "" method = "POST" name="sentMessage" id="contactForm" novalidate>
          <div class="control-group">
            <div class="form-group floating-label-form-group controls">

              <label>Username</label>

                 <input name = "username" type="text" class="form-control" placeholder="Username" id="Username" required data-validation-required-message="Please enter your username." value ="{{ request.form.username }}">
                 <p class="help-block text-danger"></p>
                </div>
               </div>
          <div class="control-group">
            <div class="form-group floating-label-form-group controls">
              <label>Password</label>

              <input name= "pw"  class="input" placeholder="pw" id="pw" required data-validation-required-message="Please enter your password." value = "{{ request.form.pw }}">

              <p class="help-block text-danger"></p>
            </div>
          </div>

          <div class = "container">
          <div class = "row" >
          <input type = "checkbox" onclick = "Toggle()">

              <label> Show Password </label>

            <script>

                function Toggle() {
                var temp = document.getElementById("typepass");
                if (temp.type === "password") {
                temp.type = "text";
              }
                else {
                  temp.type = "password";
                }

              }

            </script>

          </div>
        </div>





          <br>
          <div id="success"></div>
          <button type="submit" class="btn btn-primary" id="sendMessageButton">Login!</button>
        </form>

        {% if error %}

        <p class = "error"> <strong> Error: </strong> {{ error }}

          {% endif %}

      </div>
    </div>
  </div>
    <div class="container">
      <div class="row">
        <div class="col-lg-8 col-md-10 mx-auto">
          <!-- Qua inizia la parte inferiore-->
          <p class="copyright text-muted">Copyright &copy; Your Website 2020</p>
        </div>
      </div>
    </div>
  </footer>



  <!-- Bootstrap core JavaScript -->
  <script src= "{{ url_for('static', filename = 'jquery.min.js')}}"> </script>
  <script src= "{{ url_for('static', filename = 'bootstrap.min.js')}}"></script>


  <!-- Custom scripts for this template -->
  <script src="{{ url_for('static', filename = 'clean-blog.min.js')}}"> </script>>

</body>

</html>

Tôi còn rất mới với mọi thứ và tôi khá bế tắc và bối rối vào lúc này. Cảm ơn bạn rất nhiều cho mọi đề nghị.

Trả lời

2 J.G. Oct 08 2020 at 22:53

Lỗi có thể do user.is_authenticated = request.form["pw"] == users[username]["pw"], đặc biệt là do request.form["pw"]- điều này cố gắng truy cập biến pw, nhưng điều này không được truyền bởi biểu mẫu.

Có thể điều này là do dấu cách sau dấu bằng - input name= "pw

Trên thực tế, tôi không chắc chắn 100%, vì tôi chưa bao giờ sử dụng một không gian ở đó. Bạn nên sử dụng IDE để định dạng mã đúng cách. Với mã "sạch" và ít nhiễu, việc phát hiện lỗi luôn dễ dàng hơn.

Cập nhật

Có vẻ như được phép sử dụng dấu cách : Sử dụng dấu cách trước và sau dấu `=` của thuộc tính phần tử HTML

Tái bút: Có thể lỗi liên quan đến việc bạn sử dụng request_loader. Tôi là một người dùng nhiều đăng nhập bằng flask và tôi chưa bao giờ sử dụng request_loader. Như đã nói đây là một dạng đặc biệt, bạn thường không cần.

Tôi khuyên bạn nên đọc lại tài liệu ( https://flask-login.readthedocs.io/en/latest/#custom-login-using-request-loader ) và cũng có thể xem hướng dẫn tuyệt vời này: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

Cập nhật 2

Tôi đã đọc tài liệu đăng nhập bình. Khi bạn sử dụng request_loader, bạn cần gửi dữ liệu đăng nhập với mọi yêu cầu, nhưng bạn chỉ gửi khi điền vào biểu mẫu.

=> Như đã nói trước đó. Không sử dụng trình tải yêu cầu. Sử dụng ví dụ: đăng nhập dựa trên cookie. Xem tài liệu Đăng nhập Flask và hướng dẫn lớn.

1 simkusr Oct 08 2020 at 21:16

Bạn nên có cơ sở dữ liệu để lưu trữ các contentblog. Sau đó, tại sao bạn muốn tiết lộ thông tin đăng nhập của mình trong .yaml (không bao giờ để lộ thông tin đăng nhập trong các tệp được bao gồm trong kho lưu trữ git vì lý do lịch sử)? Tôi sẽ sử dụng ví dụ flask_loginhoặc flask_uservà tạo một người dùng với một tập lệnh trong sản xuất (môi trường khác), ví dụ:

python create_account.py --username=username --password=password

vì vậy không ai có thể tạo tài khoản, khi đó xác thực tên người dùng / mật khẩu sẽ được xử lý bởi một trong hai thư viện đó và bạn sẽ không để lộ nó (trong trường hợp một số người sẽ truy cập kho lưu trữ git của bạn hoặc tương tự).

Ít nhất tôi sẽ sử dụng cách tiếp cận này.