Python Flask: werkzeug.exceptions.BadRequestKeyError
Jestem nowy w Python Flask i próbuję zaprojektować mojego osobistego bloga. Ponieważ jest to tylko do użytku prywatnego, chciałbym umieścić stronę „dodaj” dla posta jako obszar chroniony z nazwą użytkownika i hasłem tylko po to, aby dodawać i zarządzać moimi postami. Aby nie pokazywać danych logowania w kodzie źródłowym, zdecydowałem się zapisać je w pliku .yaml i pobrać je ( {"Franz":{"pw":"Biberkopf"}}) za pomocą funkcji login.
Ale kiedy wpisuję „Franz” jako nazwę użytkownika i „Biberkopf” jako hasło, pojawia się ten błąd:
werkzeug.exceptions.BadRequestKeyError
werkzeug.exceptions.BadRequestKeyError: 400 Złe żądanie: Przeglądarka (lub serwer proxy) wysłała żądanie, którego ten serwer nie mógł zrozumieć.
KeyError: 'pw'Traceback (ostatnie ostatnie połączenie)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 2464, w wywołaniu
return self.wsgi_app (environment, start_response)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 2450, w wsgi_app
response = self.handle_exception (e)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 1867, w handle_exception
reraise (exc_type, exc_value, tb)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py”, wiersz 39, w reraise
podnieść wartość
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 2447, w wsgi_app
response = self.full_dispatch_request ()
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 1952, w full_dispatch_request
rv = self.handle_user_exception (e)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 1821, w handle_user_exception
reraise (exc_type, exc_value, tb)
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py”, wiersz 39, w reraise
podnieść wartość
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 1950, w full_dispatch_request
rv = self.dispatch_request ()
Plik „C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py”, wiersz 1936, w dispatch_request
return self.view_functionsrule.endpoint
Plik „C: \ Users \ franc \ OneDrive \ Desktop \ Blog_Project \ blog_prova.py”, wiersz 102, w logowaniu
user.id = nazwa użytkownika
flask_login.login_user (użytkownik)
przekierowanie zwrotne (url_for ("add"))
else:
error = "Leider haben Sie die falschen Zugangsdaten."
return render_template ("login.html", error = error)
return render_template ("login.html")
Kod:
@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 przechwycił wyjątek w aplikacji WSGI. Możesz teraz spojrzeć na śledzenie, które doprowadziło do błędu.
Aby przełączać się między interaktywnym śledzeniem a zwykłym tekstem, możesz kliknąć nagłówek „Traceback”. Na podstawie śledzenia tekstu można również utworzyć jego wklejenie. Aby wykonać kod, najedź myszą na ramkę, którą chcesz debugować i kliknij ikonę konsoli po prawej stronie.
Możesz wykonać dowolny kod Pythona w ramkach stosu i jest dostępnych kilka dodatkowych pomocników do introspekcji:
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)
Pomysł byłby taki, że gdyby ktoś próbował się zalogować bez prawidłowej nazwy użytkownika i hasła, wyświetliłby się komunikat o błędzie. Mając odpowiednie poświadczenia, możesz mieć dostęp do strony „dodaj”. Tutaj moja strona login.html:
<!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 © 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>
Jestem bardzo nowy we wszystkim iw tej chwili utknąłem i jestem zdezorientowany. Bardzo dziękuję za każdą sugestię.
Odpowiedzi
Błąd jest prawdopodobnie spowodowany user.is_authenticated = request.form["pw"] == users[username]["pw"], zwłaszcza przez request.form["pw"]- to próbuje uzyskać dostęp do zmiennej pw, ale nie zostało to przesłane przez formularz.
Może jest to spowodowane spacją po znaku równości - input name= "pw
Właściwie nie jestem w 100% pewien, ponieważ nigdy tam nie korzystałem. Dobrą praktyką jest używanie IDE, które poprawnie formatuje kod. Z „czystym” kodem i mniejszym szumem zawsze łatwiej jest wykryć błędy.
Aktualizacja
Wygląda na to, że spacje są dozwolone: spacje przed i po znaku `=` atrybutu elementu HTML
PS: Być może błąd jest związany z korzystaniem z request_loader. Jestem intensywnym użytkownikiem flask-login i nigdy nie korzystałem z request_loader. Jak już wspomniano, jest to specjalny formularz, którego zwykle nie potrzebujesz.
Zalecam ponowne przeczytanie dokumentacji ( https://flask-login.readthedocs.io/en/latest/#custom-login-using-request-loader ), a także spójrz na ten fantastyczny samouczek: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
Zaktualizuj 2
Przeczytałem dokumentację logowania do flask. Korzystając z request_loader, musisz przesyłać dane do logowania przy każdym żądaniu, ale wysyłasz je dopiero po wypełnieniu formularza.
=> Jak wcześniej powiedziałem. Nie używaj modułu ładującego żądania. Użyj np. Logowania opartego na plikach cookie. Zobacz dokumentację Flask Login i mega tutorial.
Powinieneś mieć bazę danych do przechowywania contentblogów. Dlaczego więc chcesz ujawnić w .yaml swoje poświadczenia (nigdy, przenigdy nie ujawniaj poświadczeń w plikach, które są zawarte w repozytorium git ze względów historycznych)? Użyłbym np. flask_loginLub flask_useri stworzyłbym użytkownika ze skryptem w środowisku produkcyjnym (inne środowiska) np
python create_account.py --username=username --password=password
więc nikt nie mógł założyć konta, wtedy walidacja nazwy użytkownika / hasła byłaby obsługiwana przez którąkolwiek z tych bibliotek i nie można ich ujawnić (na wypadek, gdyby ktoś uzyskał dostęp do twojego repozytorium git lub w ten sposób).
Przynajmniej użyłbym tego podejścia.