Python Flask: werkzeug.exceptions.BadRequestKeyError

Oct 08 2020

Saya baru di Python Flask dan saya mencoba mendesain blog pribadi saya. Karena ini hanya untuk penggunaan pribadi, saya ingin menempatkan halaman "tambah" untuk posting sebagai area terlindungi dengan nama pengguna dan kata sandi hanya untuk menambah dan mengelola posting saya. Agar tidak menampilkan kredensial login dalam kode sumber, saya memutuskan untuk menyimpan dalam file .yaml dan mengambilnya ( {"Franz":{"pw":"Biberkopf"}}) melalui fungsi login.

Tetapi ketika saya memasukkan "Franz" sebagai nama pengguna dan "Biberkopf" sebagai sandi, saya mendapatkan kesalahan ini:

werkzeug.exceptions.BadRequestKeyError

werkzeug.exceptions.BadRequestKeyError: 400 Permintaan Buruk: Browser (atau proxy) mengirim permintaan yang tidak dapat dipahami server ini.
KeyError: 'pw'

Traceback (panggilan terakhir terakhir)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 2464, sedang dipanggil

return self.wsgi_app (environment, start_response)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 2450, di wsgi_app

respon = self.handle_exception (e)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 1867, di handle_exception

reraise (exc_type, exc_value, tb)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py", baris 39, di reraise

meningkatkan nilai

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 2447, di wsgi_app

response = self.full_dispatch_request ()

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 1952, di full_dispatch_request

rv = self.handle_user_exception (e)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 1821, di handle_user_exception

reraise (exc_type, exc_value, tb)

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py", baris 39, di reraise

meningkatkan nilai

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 1950, di full_dispatch_request

rv = self.dispatch_request ()

File "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", baris 1936, di dispatch_request

return self.view_functionsrule.endpoint

File "C: \ Users \ franc \ OneDrive \ Desktop \ Blog_Project \ blog_prova.py", baris 102, masuk

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")

Kode:

@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'

Debugger menangkap pengecualian dalam aplikasi WSGI Anda. Anda sekarang dapat melihat pelacakan balik yang menyebabkan kesalahan.

Untuk beralih antara pelacakan balik interaktif dan teks biasa, Anda dapat mengklik judul "Lacak balik". Dari traceback teks Anda juga dapat membuat paste. Untuk eksekusi kode, arahkan mouse ke bingkai yang ingin Anda debug dan klik ikon konsol di sisi kanan.

Anda dapat mengeksekusi kode Python sewenang-wenang di tumpukan frame dan ada beberapa pembantu tambahan yang tersedia untuk introspeksi:

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)


Idenya adalah, jika seseorang mencoba masuk tanpa nama pengguna dan kata sandi yang benar, pesan kesalahan akan ditampilkan. Dengan kredensial yang tepat, Anda dapat memiliki akses ke halaman "tambah". Di sini halaman login.html saya:

<!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>

Saya sangat baru dalam segala hal dan saya cukup terjebak dan bingung saat ini. Terima kasih banyak atas setiap saran.

Jawaban

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

Kesalahan mungkin disebabkan oleh user.is_authenticated = request.form["pw"] == users[username]["pw"], terutama oleh request.form["pw"]- ini mencoba mengakses variabel pw, tetapi ini tidak dikirimkan oleh formulir.

Mungkin ini disebabkan oleh spasi setelah tanda sama dengan - input name= "pw

Sebenarnya saya tidak 100% yakin, karena saya tidak pernah menggunakan spasi di sana. Ini adalah praktik yang baik untuk menggunakan IDE yang memformat kode dengan benar. Dengan kode "bersih" dan lebih sedikit noise, selalu lebih mudah untuk menemukan kesalahan.

Memperbarui

Sepertinya spasi diperbolehkan: Menggunakan spasi sebelum dan sesudah tanda `=` dari atribut elemen HTML

PS: Mungkin kesalahan itu terkait dengan penggunaan request_loader Anda. Saya adalah pengguna flask-login yang berat dan saya tidak pernah menggunakan request_loader. Seperti yang dinyatakan, ini adalah formulir khusus, yang biasanya tidak Anda butuhkan.

Saya sarankan Anda membaca dokumentasi lagi ( https://flask-login.readthedocs.io/en/latest/#custom-login-using-request-loader ) dan juga lihat tutorial fantastis ini: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world

Perbarui 2

Saya membaca dokumentasi flask-login. Saat Anda menggunakan request_loader, Anda perlu mengirim data login dengan setiap permintaan, tetapi Anda hanya mengirimkannya saat Anda mengisi formulir.

=> Seperti yang dikatakan sebelumnya. Jangan gunakan pemuat permintaan. Gunakan mis. Login berbasis cookie. Lihat dokumen Flask Login dan mega tutorial.

1 simkusr Oct 08 2020 at 21:16

Anda harus memiliki database untuk menyimpan contentblog. Lalu mengapa Anda ingin mengekspos kredensial Anda dalam .yaml (jangan pernah mengekspos kredensial dalam file yang disertakan dalam repositori git karena alasan historis)? Saya akan menggunakan misalnya flask_loginatau flask_userdan membuat pengguna dengan skrip dalam produksi (lingkungan lain) misalnya

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

jadi tidak ada yang bisa membuat akun, maka validasi nama pengguna / kata sandi akan ditangani oleh salah satu pustaka itu dan Anda tidak akan mengeksposnya (jika seseorang akan mengakses repositori git Anda atau semacamnya).

Setidaknya saya akan menggunakan pendekatan ini.