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']])
                    }),

这是一个简单的错误,但我花了很长时间才发现。