Python Flask: werkzeug.exceptions.BadRequestKeyError
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 © 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
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.
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.