Matraz de Python: werkzeug.exceptions.BadRequestKeyError
Soy nuevo en Python Flask y estoy tratando de diseñar mi blog personal. Como es solo para uso privado, me gustaría poner la página "agregar" para publicar como un área protegida con un nombre de usuario y una contraseña solo para agregar y administrar mis publicaciones. Para no mostrar las credenciales de inicio de sesión en el código fuente, decidí almacenarlas en un archivo .yaml y recuperarlas ( {"Franz":{"pw":"Biberkopf"}}) a través de la función de inicio de sesión.
Pero cuando pongo "Franz" como nombre de usuario y "Biberkopf" como contraseña, aparece este error:
werkzeug.exceptions.BadRequestKeyError
werkzeug.exceptions.BadRequestKeyError: 400 Solicitud incorrecta: el navegador (o proxy) envió una solicitud que este servidor no pudo comprender.
KeyError: 'pw'Rastreo (llamadas recientes más última)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 2464, en la llamada
return self.wsgi_app (entorno, start_response)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 2450, en wsgi_app
respuesta = self.handle_exception (e)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 1867, en handle_exception
volver a subir (exc_type, exc_value, tb)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py", línea 39, en reraise
aumentar el valor
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 2447, en wsgi_app
respuesta = self.full_dispatch_request ()
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 1952, en full_dispatch_request
rv = self.handle_user_exception (e)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 1821, en handle_user_exception
volver a subir (exc_type, exc_value, tb)
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask_compat.py", línea 39, en reraise
aumentar el valor
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 1950, en full_dispatch_request
rv = self.dispatch_request ()
Archivo "C: \ Users \ franc \ AppData \ Local \ Programs \ Python \ Python38-32 \ Lib \ site-packages \ flask \ app.py", línea 1936, en dispatch_request
return self.view_functionsrule.endpoint
Archivo "C: \ Users \ franc \ OneDrive \ Desktop \ Blog_Project \ blog_prova.py", línea 102, en inicio de sesión
user.id = username
flask_login.login_user (usuario)
return redirect (url_for ("agregar"))
else:
error = "Leider haben Sie die falschen Zugangsdaten."
return render_template ("login.html", error = error)
return render_template ("login.html")
Código:
@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'
El depurador detectó una excepción en su aplicación WSGI. Ahora puede ver el rastreo que condujo al error.
Para cambiar entre el rastreo interactivo y el texto plano, puede hacer clic en el título "Rastreo". A partir del rastreo de texto también puede crear una pasta. Para ejecutar el código, coloque el mouse sobre el marco que desea depurar y haga clic en el ícono de la consola en el lado derecho.
Puede ejecutar código Python arbitrario en los marcos de pila y hay algunos ayudantes adicionales disponibles para la introspección:
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)
La idea sería que, si alguien intenta iniciar sesión sin el nombre de usuario y la contraseña correctos, se mostrará un mensaje de error. Con las credenciales correctas, puede tener acceso a la página "agregar". Aquí mi página 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>
Soy muy nuevo en todo y estoy bastante atascado y confundido en este momento. Muchas gracias por cada sugerencia.
Respuestas
El error probablemente sea causado por user.is_authenticated = request.form["pw"] == users[username]["pw"], especialmente por request.form["pw"]- esto intenta acceder a la variable pw, pero esto no fue transmitido por el formulario.
Quizás esto es causado por el espacio después del signo igual - input name= "pw
En realidad, no estoy 100% seguro, ya que nunca usé un espacio allí. Es una buena práctica utilizar un IDE que formatee correctamente el código. Con un código "limpio" y menos ruido, siempre es más fácil detectar errores.
Actualizar
Parece que se permiten espacios: uso de espacios antes y después del signo `=` del atributo de elemento HTML
PD: Tal vez el error esté relacionado con su uso de request_loader. Soy un gran usuario de flask-login y nunca usé request_loader. Como se ha dicho, es un formulario especial que normalmente no es necesario.
Te recomiendo que vuelvas a leer la documentación ( https://flask-login.readthedocs.io/en/latest/#custom-login-using-request-loader ) y también eche un vistazo a este fantástico tutorial: https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-i-hello-world
Actualización 2
Leí la documentación de inicio de sesión del matraz. Cuando utiliza request_loader, debe enviar los datos de inicio de sesión con cada solicitud, pero solo los envía cuando completa el formulario.
=> Como se dijo anteriormente. No utilice el cargador de solicitudes. Utilice, por ejemplo, inicio de sesión basado en cookies. Consulte la documentación de Flask Login y el mega tutorial.
Debe tener una base de datos para almacenar los contentblogs. Entonces, ¿por qué desea exponer en .yaml sus credenciales (nunca exponga las credenciales en archivos que se incluyen en el repositorio de git por razones históricas)? Yo usaría eg flask_loginor flask_usery crearía un usuario con un script en producción (otros entornos) eg
python create_account.py --username=username --password=password
para que nadie pueda crear una cuenta, entonces la validación de nombre de usuario / contraseña sería manejada por cualquiera de esa biblioteca y usted no la expondría (en caso de que alguien acceda a su repositorio de git o algo así).
Al menos usaría este enfoque.