jQUERY,烧瓶:尝试将 Javascript 变量发送到 Python 脚本时请求错误 (400)
jQUERY, FLASK: Bad request (400) while trying to send a Javascript variable to a Python script
您好,欢迎光临!
简介
感谢您查看我的问题;您的每一个答案都对我精通 Web 开发的旅程非常重要!我当然非常感谢 Whosebug 社区为所有刚刚深入编程世界的初级开发人员提供的所有支持。
程序的作用
#-----------------------------------------------------------------------------
# Name: Lottery Simulator 2022
# Purpose: To encourage young people not to gamble on lotteries, as the probablity of correctly guessing the number is infinitemisial!
#
# Author: John Seong
# Created: 25-Feb-2022
# Updated: 01-Mar-2022
#-----------------------------------------------------------------------------
# I think this project deserves a level 4+ because...
#
# Features Added:
# Game being entirely web-based using the Flask micro web framework
# Utilization of both functional programming and object-oriented programming
# Calculate the chances of winning for the sake of learning why gambling is risky
# After a set of number is entered by the user, the combinations will reset and the program will give completely an arbitrary number set differing from the previous one
# The user can change the difficulty setting, which will determine the constraint of the possible number set
# Not only does it allow user to guess one number at a time, but multiple numbers stored in a dictionary
# In-game currency system that syncronizes with the SQLAlchemy database, which also generates the player leaderboard
# Game hosted on a cloud platform Heroku
# Used AJAX for communication between JAVASCRIPT and PYTHON files (via JSON)
#-----------------------------------------------------------------------------
我正在处理的问题
当我单击 'next' 按钮三次时,我在控制台上收到此错误消息,这导致 ajax
方法有助于 JAVASCRIPT 和 [=53] 之间的通信=] 个文件:
127.0.0.1 - - [04/Mar/2022 13:18:28] "POST /game HTTP/1.1" 400 -
HTML 和 JAVASCRIPT
game.html
{% extends "index.html" %} {% block content %}
<head>
<script>
// TO DO: MAKE THE BAREBONE APP FIRST AND THEN PRETTIFY IT!
var counter = 0; // Button counter
var dict = {
'name': 0,
'range': 1,
'draws': 2
};
function onClickEvent() {
// Define all the elements by their ID...
const input = document.getElementById("name");
const warning = document.getElementById("warning");
const guide_text = document.getElementById("guide-text");
const guide_text_2 = document.getElementById("guide-text-2");
const array_renderer = document.getElementById("array-renderer");
const numbers_list = document.getElementById("numbers-list");
const name_of_input = document.getElementById("name").innerHTML;
const answers = []; // List for storing all the answers
// When no value is entered in the input, throw an error message...
if (document.forms['frm'].name.value === "") {
warning.style.display = 'block';
} else {
warning.style.display = 'none';
answers.push(document.forms['frm'].name.value);
counter++;
document.forms['frm'].name.value = "";
}
// Scene transition when the submit button is pressed once... twice... three times... etc.
if (counter == 1) {
guide_text.innerHTML = "SET THE<br>RANGE OF<br>POSSIBLE<br>NUMBERS";
guide_text_2.innerHTML = "DON'T GO TOO CRAZY!";
input.placeholder = "Enter min. and max. values seperated by a space...";
} else if (counter == 2) {
guide_text.innerHTML = "HOW MANY<br>DRAWS?";
guide_text_2.innerHTML = "IS MURPHY'S LAW REAL?";
input.placeholder = "Enter the number of draws...";
answers.push(document.forms['frm'].name.value);
} else if (counter == 3) {
$.ajax({
url: '{{ url_for("main.game") }}',
type: 'POST',
data: {
nickname: answers[dict['name']],
range: answers[dict['range']],
draws: answers[dict['draws']]
},
success: function(response) {
console.log("Successful attempt at retrieving the data!");
warning.style.display = 'none';
},
error: function(response) {
warning.style.display = 'block';
warning.innerHTML = "ERROR WHILE RETRIEVING THE LIST!"
}
});
guide_text.innerHTML = "GUESS THE<br>NUMBERS IN A<br>" + answers[dict['draws']] + " * 1 ARRAY!";
array_renderer.style.display = 'block';
// Parse the JSON file handed over by views.py (set that contains random numbers)
numbers_list.innerHTML = JSON.parse(data.random_set_json);
input.placeholder = "Enter the values seperated by a space...";
}
}
// Execute a function when the user releases a key on the keyboard => NEEDS FIX! DOESN'T WORK!
input.addEventListener("keyup", function(event) {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
document.getElementById("button").click();
}
});
</script>
<link rel="stylesheet" href="../static/bootstrap/css/style.css">
</head>
<header class="masthead" style="background: lightgray">
<div class="container h-100">
<div class="row h-100">
<div class="col-lg-7 my-auto" style="border-style: none;">
<div class="mx-auto header-content" style="background: rgba(47,37,48, 1);padding: 30px;border-radius: 34px; ;border-color: white;margin: 6px; color: white">
<h1 class="mb-5" id="guide-text" style="font-family: 'Roboto Mono', monospace;color: white;text-align: center;">WHAT IS<br>YOUR NAME?</h1>
<h3 id="guide-text-2" style="font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: white">DON'T HESITATE! GO ON!</h3><br>
<form action="" name="frm" method="post">
<input class="form-control" type="text" id="name" name="name" placeholder="Enter your nickname...">
<br>
<div id="array-renderer">
<h3 id="numbers-list" style="font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: white">NULL</h3><br>
</div>
<br><a class="btn btn-outline-warning btn-xl" role="button" id="button" onclick="onClickEvent()" href="#download" style="color: white;font-family: 'Roboto Mono', monospace;font-size: 20px; justify-self: center; align-self: center">Next</a>
<br><br>
<p id="warning" style="display: none; font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: lightcoral">The value you entered is invalid. Please try it again.</p>
</form>
</div>
</div>
</div>
</div>
</header>
{% endblock content %}
PYTHON脚本
views.py
from email.policy import default
from random import Random
from flask import Blueprint, render_template, request, session, jsonify
# from flask import current_app as app
import configparser
import json
from website.extensions import db
from .models import PlayerCurrency, RandomSet
# Import the config.cfg file and read the default value (starting point) of the game currency
config = configparser.ConfigParser()
# Get the absolute path of the CFG file by doing os.getcwd() and joining it to config.cfg
cfg_path = 'website/config.cfg'
bp = Blueprint('main', __name__)
# Whether user cookies will be collected or not
cookiesAvail = True
# Read the CFG file
config.read(cfg_path)
# Fix the 'failed to load' error!
try:
default_count = config.getint("default", "NUM_OF_NUMS")
default_coins = config.getint("default","MONEY")
except:
print('CFG file failed to load twice!')
@bp.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
allow_cookies = request.form.get("allow-cookies")
decline_cookies = request.form.get("decline-cookies")
session.clear()
if allow_cookies == 'allow' and decline_cookies != 'decline':
cookiesAvail = True
if decline_cookies == 'decline' and allow_cookies != 'allow':
cookiesAvail = False
return render_template("home.html")
@bp.route('/about')
def about():
session.clear()
return render_template("about.html")
@bp.route('/game', methods=['GET', 'POST'])
def game():
if request.method == 'POST':
# Clear the session
session.clear()
# Get all the user input values from game.js
player_name = json.loads(request.form['nickname'])
player_range = json.loads(request.form['range']).split(' ') # player_range[0] => min value, player_range[1] => max value
player_draws = json.loads(request.form['draws'])
# Define a random list object (instantiating a class located in models.py)
random_set = RandomSet(player_range[0], player_range[1], player_draws)
# Create a random list by generating arbitrary values
random_set.generate()
# Convert the generated random list (Python) into JSON-compatible string, so we can hand it over to game.js
random_set_json = json.dumps(random_set.current_set)
# INTERACTION BETWEEN JAVASCRIPT AND PYTHON (FLASK) USING AJAX AND JSONIFY: https://ayumitanaka13.medium.com/how-to-use-ajax-with-python-flask-729c0a8e5346
# HOW PYTHON-JSON CONVERSION WORKS USING THE JSON MODULE: https://www.w3schools.com/python/python_json.asp
return render_template("game.html")
# AJAX METHOD: https://ayumitanaka13.medium.com/how-to-use-ajax-with-python-flask-729c0a8e5346
# WHAT IS CURRENT_APP? LINK: https://flask.palletsprojects.com/en/2.0.x/appcontext/
# cd .. // go to the upper directory
# requirements.txt => # pip3 install -r requirements.txt to install the files
# COOKIES => WILL BE USED TO SKIP ENTER THE NAME STAGE IN SETUP!
# ADD DIFFICULY INDICATOR DEPENDING ON THE SCALE OF THE RANGE, AND SEPERATE THE LEADERBOARD BY DIFFICULTY LEVEL (EASY, MODERATE, HARD)
models.py
class RandomSet():
def __init__(self, min_value, max_value, draws):
self.min_value = min_value # Minimum value that the raodom number can be
self.max_value = max_value # Maximum value that the random numbers can be
self.count = draws # Number of draws
# A list that contains the set that computer generated, containing machine-picked arbitrary numbers
self.current_set = []
self.chances = 0 # Chances of winning, calculated by the computer
# Generate a set containing completely arbitrary numbers
def generate(self):
for i in range(self.count):
# Add a random value generated by a computer to the list using a for loop and a RANDINT built-in function
self.current_set.append(random.randint(
self.min_value, self.max_value))
# Calculate the chances and store it in the instance's variable
self.chances = calculate_chances(self.current_set, self.count)
def calculate_chances(current_set, count):
"""
Calculate the chances of winning,
by using the permuation formula
and converting it to a percentage.
"""
return str(f'{(1 / len(permutations(current_set, count))) * 100} %')
您还可以在我的 GitHub repository.
上查看完整代码
事实证明我必须 double-JSON stringify 我移交给服务器端的数据。
所以基本上它的意思是:
原始代码如下所示:
$.ajax({
url: '{{ url_for("main.game") }}',
type: 'POST',
data: {
nickname: answers[dict['name']],
range: answers[dict['range']],
draws: answers[dict['draws']]
},
代码应该像这样才能工作:
$.ajax({
url: '/game',
type: 'POST',
data: JSON.stringify({ // Make sure you surround the data variable(s) with JSON.stringify's MULTIPLE TIMES to avoid any potential error! Data HAS to be in JSON format.
nickname: JSON.stringify(answers[dict['name']]),
range: JSON.stringify(answers[dict['range']]),
draws: JSON.stringify(answers[dict['draws']])
}),
这是一个简单的错误,但我花了很长时间才发现。
您好,欢迎光临!
简介
感谢您查看我的问题;您的每一个答案都对我精通 Web 开发的旅程非常重要!我当然非常感谢 Whosebug 社区为所有刚刚深入编程世界的初级开发人员提供的所有支持。
程序的作用
#-----------------------------------------------------------------------------
# Name: Lottery Simulator 2022
# Purpose: To encourage young people not to gamble on lotteries, as the probablity of correctly guessing the number is infinitemisial!
#
# Author: John Seong
# Created: 25-Feb-2022
# Updated: 01-Mar-2022
#-----------------------------------------------------------------------------
# I think this project deserves a level 4+ because...
#
# Features Added:
# Game being entirely web-based using the Flask micro web framework
# Utilization of both functional programming and object-oriented programming
# Calculate the chances of winning for the sake of learning why gambling is risky
# After a set of number is entered by the user, the combinations will reset and the program will give completely an arbitrary number set differing from the previous one
# The user can change the difficulty setting, which will determine the constraint of the possible number set
# Not only does it allow user to guess one number at a time, but multiple numbers stored in a dictionary
# In-game currency system that syncronizes with the SQLAlchemy database, which also generates the player leaderboard
# Game hosted on a cloud platform Heroku
# Used AJAX for communication between JAVASCRIPT and PYTHON files (via JSON)
#-----------------------------------------------------------------------------
我正在处理的问题
当我单击 'next' 按钮三次时,我在控制台上收到此错误消息,这导致 ajax
方法有助于 JAVASCRIPT 和 [=53] 之间的通信=] 个文件:
127.0.0.1 - - [04/Mar/2022 13:18:28] "POST /game HTTP/1.1" 400 -
HTML 和 JAVASCRIPT
game.html
{% extends "index.html" %} {% block content %}
<head>
<script>
// TO DO: MAKE THE BAREBONE APP FIRST AND THEN PRETTIFY IT!
var counter = 0; // Button counter
var dict = {
'name': 0,
'range': 1,
'draws': 2
};
function onClickEvent() {
// Define all the elements by their ID...
const input = document.getElementById("name");
const warning = document.getElementById("warning");
const guide_text = document.getElementById("guide-text");
const guide_text_2 = document.getElementById("guide-text-2");
const array_renderer = document.getElementById("array-renderer");
const numbers_list = document.getElementById("numbers-list");
const name_of_input = document.getElementById("name").innerHTML;
const answers = []; // List for storing all the answers
// When no value is entered in the input, throw an error message...
if (document.forms['frm'].name.value === "") {
warning.style.display = 'block';
} else {
warning.style.display = 'none';
answers.push(document.forms['frm'].name.value);
counter++;
document.forms['frm'].name.value = "";
}
// Scene transition when the submit button is pressed once... twice... three times... etc.
if (counter == 1) {
guide_text.innerHTML = "SET THE<br>RANGE OF<br>POSSIBLE<br>NUMBERS";
guide_text_2.innerHTML = "DON'T GO TOO CRAZY!";
input.placeholder = "Enter min. and max. values seperated by a space...";
} else if (counter == 2) {
guide_text.innerHTML = "HOW MANY<br>DRAWS?";
guide_text_2.innerHTML = "IS MURPHY'S LAW REAL?";
input.placeholder = "Enter the number of draws...";
answers.push(document.forms['frm'].name.value);
} else if (counter == 3) {
$.ajax({
url: '{{ url_for("main.game") }}',
type: 'POST',
data: {
nickname: answers[dict['name']],
range: answers[dict['range']],
draws: answers[dict['draws']]
},
success: function(response) {
console.log("Successful attempt at retrieving the data!");
warning.style.display = 'none';
},
error: function(response) {
warning.style.display = 'block';
warning.innerHTML = "ERROR WHILE RETRIEVING THE LIST!"
}
});
guide_text.innerHTML = "GUESS THE<br>NUMBERS IN A<br>" + answers[dict['draws']] + " * 1 ARRAY!";
array_renderer.style.display = 'block';
// Parse the JSON file handed over by views.py (set that contains random numbers)
numbers_list.innerHTML = JSON.parse(data.random_set_json);
input.placeholder = "Enter the values seperated by a space...";
}
}
// Execute a function when the user releases a key on the keyboard => NEEDS FIX! DOESN'T WORK!
input.addEventListener("keyup", function(event) {
// Number 13 is the "Enter" key on the keyboard
if (event.keyCode === 13) {
// Cancel the default action, if needed
event.preventDefault();
// Trigger the button element with a click
document.getElementById("button").click();
}
});
</script>
<link rel="stylesheet" href="../static/bootstrap/css/style.css">
</head>
<header class="masthead" style="background: lightgray">
<div class="container h-100">
<div class="row h-100">
<div class="col-lg-7 my-auto" style="border-style: none;">
<div class="mx-auto header-content" style="background: rgba(47,37,48, 1);padding: 30px;border-radius: 34px; ;border-color: white;margin: 6px; color: white">
<h1 class="mb-5" id="guide-text" style="font-family: 'Roboto Mono', monospace;color: white;text-align: center;">WHAT IS<br>YOUR NAME?</h1>
<h3 id="guide-text-2" style="font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: white">DON'T HESITATE! GO ON!</h3><br>
<form action="" name="frm" method="post">
<input class="form-control" type="text" id="name" name="name" placeholder="Enter your nickname...">
<br>
<div id="array-renderer">
<h3 id="numbers-list" style="font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: white">NULL</h3><br>
</div>
<br><a class="btn btn-outline-warning btn-xl" role="button" id="button" onclick="onClickEvent()" href="#download" style="color: white;font-family: 'Roboto Mono', monospace;font-size: 20px; justify-self: center; align-self: center">Next</a>
<br><br>
<p id="warning" style="display: none; font-family: 'Roboto Mono', monospace; text-transform: uppercase; color: lightcoral">The value you entered is invalid. Please try it again.</p>
</form>
</div>
</div>
</div>
</div>
</header>
{% endblock content %}
PYTHON脚本
views.py
from email.policy import default
from random import Random
from flask import Blueprint, render_template, request, session, jsonify
# from flask import current_app as app
import configparser
import json
from website.extensions import db
from .models import PlayerCurrency, RandomSet
# Import the config.cfg file and read the default value (starting point) of the game currency
config = configparser.ConfigParser()
# Get the absolute path of the CFG file by doing os.getcwd() and joining it to config.cfg
cfg_path = 'website/config.cfg'
bp = Blueprint('main', __name__)
# Whether user cookies will be collected or not
cookiesAvail = True
# Read the CFG file
config.read(cfg_path)
# Fix the 'failed to load' error!
try:
default_count = config.getint("default", "NUM_OF_NUMS")
default_coins = config.getint("default","MONEY")
except:
print('CFG file failed to load twice!')
@bp.route('/', methods=['GET', 'POST'])
def index():
if request.method == "POST":
allow_cookies = request.form.get("allow-cookies")
decline_cookies = request.form.get("decline-cookies")
session.clear()
if allow_cookies == 'allow' and decline_cookies != 'decline':
cookiesAvail = True
if decline_cookies == 'decline' and allow_cookies != 'allow':
cookiesAvail = False
return render_template("home.html")
@bp.route('/about')
def about():
session.clear()
return render_template("about.html")
@bp.route('/game', methods=['GET', 'POST'])
def game():
if request.method == 'POST':
# Clear the session
session.clear()
# Get all the user input values from game.js
player_name = json.loads(request.form['nickname'])
player_range = json.loads(request.form['range']).split(' ') # player_range[0] => min value, player_range[1] => max value
player_draws = json.loads(request.form['draws'])
# Define a random list object (instantiating a class located in models.py)
random_set = RandomSet(player_range[0], player_range[1], player_draws)
# Create a random list by generating arbitrary values
random_set.generate()
# Convert the generated random list (Python) into JSON-compatible string, so we can hand it over to game.js
random_set_json = json.dumps(random_set.current_set)
# INTERACTION BETWEEN JAVASCRIPT AND PYTHON (FLASK) USING AJAX AND JSONIFY: https://ayumitanaka13.medium.com/how-to-use-ajax-with-python-flask-729c0a8e5346
# HOW PYTHON-JSON CONVERSION WORKS USING THE JSON MODULE: https://www.w3schools.com/python/python_json.asp
return render_template("game.html")
# AJAX METHOD: https://ayumitanaka13.medium.com/how-to-use-ajax-with-python-flask-729c0a8e5346
# WHAT IS CURRENT_APP? LINK: https://flask.palletsprojects.com/en/2.0.x/appcontext/
# cd .. // go to the upper directory
# requirements.txt => # pip3 install -r requirements.txt to install the files
# COOKIES => WILL BE USED TO SKIP ENTER THE NAME STAGE IN SETUP!
# ADD DIFFICULY INDICATOR DEPENDING ON THE SCALE OF THE RANGE, AND SEPERATE THE LEADERBOARD BY DIFFICULTY LEVEL (EASY, MODERATE, HARD)
models.py
class RandomSet():
def __init__(self, min_value, max_value, draws):
self.min_value = min_value # Minimum value that the raodom number can be
self.max_value = max_value # Maximum value that the random numbers can be
self.count = draws # Number of draws
# A list that contains the set that computer generated, containing machine-picked arbitrary numbers
self.current_set = []
self.chances = 0 # Chances of winning, calculated by the computer
# Generate a set containing completely arbitrary numbers
def generate(self):
for i in range(self.count):
# Add a random value generated by a computer to the list using a for loop and a RANDINT built-in function
self.current_set.append(random.randint(
self.min_value, self.max_value))
# Calculate the chances and store it in the instance's variable
self.chances = calculate_chances(self.current_set, self.count)
def calculate_chances(current_set, count):
"""
Calculate the chances of winning,
by using the permuation formula
and converting it to a percentage.
"""
return str(f'{(1 / len(permutations(current_set, count))) * 100} %')
您还可以在我的 GitHub repository.
上查看完整代码事实证明我必须 double-JSON stringify 我移交给服务器端的数据。
所以基本上它的意思是:
原始代码如下所示:
$.ajax({
url: '{{ url_for("main.game") }}',
type: 'POST',
data: {
nickname: answers[dict['name']],
range: answers[dict['range']],
draws: answers[dict['draws']]
},
代码应该像这样才能工作:
$.ajax({
url: '/game',
type: 'POST',
data: JSON.stringify({ // Make sure you surround the data variable(s) with JSON.stringify's MULTIPLE TIMES to avoid any potential error! Data HAS to be in JSON format.
nickname: JSON.stringify(answers[dict['name']]),
range: JSON.stringify(answers[dict['range']]),
draws: JSON.stringify(answers[dict['draws']])
}),
这是一个简单的错误,但我花了很长时间才发现。