简体   繁体   中英

Why does XMLHttpRequest::send double send POST, and how can I stop it?

I'm using python to host a simple LAN web page using HTTPRequestHandler:

from http import server # Our http server handler for http requests
import socketserver # Establish the TCP Socket connections
import json
 
PORT = 9000

messages = []
headers = []

class HTTPRequestHandler(server.BaseHTTPRequestHandler):
    def do_HEAD(self):
        self.send_response(200)
        
    def do_GET(self):
        self.send_response(200)

        if self.path != '/':
            return None
        self.send_header("Content-Type", "text/html")
        self.send_header("Connection", "Stop")
        self.end_headers()
        
        response_html = """
<html>
    <head>
        <title>
            Testa
        </title>
    </head>
    
    <body id="body">
        <h1 style="color:blue">
            Test
        </h1>


        <p>
        
                <input type="text" id="message" placeholder="Your message">

                <button onclick="sendMessage()">Send Message</button>

            <p class="result" style="color:blue">

        </p>

        <script language="javascript" type="text/javascript">
        function sendMessage(){
        
            let messagebox = document.querySelector('#message');
              
            // Creating an XHR object
            let xhr = new XMLHttpRequest();
            let url = "/message";
       
            // open a connection
            xhr.open("POST", url, true);
 
            // Set the request header i.e. which type of content you are sending
            xhr.setRequestHeader("Content-Type", "application/json");
 
            // Converting JSON data to string
            var data = JSON.stringify({ "message": messagebox.value});
 
            // Sending data with the request
            xhr.send(data);

            // Clearing the messagebox
            messagebox.value = ""
        }
        </script>
</html>
"""
        self.wfile.write(response_html.encode("utf-8"))
        return

    def do_POST(self):
        self.send_response(200)
        
        length = int(self.headers['content-length'])
        message = json.loads(self.rfile.read(length).decode("utf-8"))
        print(message)
        messages.append(message)
        headers.append([self.headers, self.command])
        
 
Handler = HTTPRequestHandler
 
with socketserver.TCPServer(("", PORT), Handler) as httpd:
    print("HTTP server started at:", PORT)
    httpd.serve_forever()

In essence, it creates this page:

网站

And adds a function to the button, so that a POST request is sent to the server containing whatever was in the box. This part works fine.

My issue is that every time this POST request is sent, it is paired with another identical one:

HTTP server started at: 9000
127.0.0.1 - - [14/Sep/2022 17:23:18] "POST /message HTTP/1.1" 200 -
{'message': 'Hello World!!'}
127.0.0.1 - - [14/Sep/2022 17:23:39] "POST /message HTTP/1.1" 200 -
{'message': 'Goodbye World!!'}
127.0.0.1 - - [14/Sep/2022 17:23:39] "POST /message HTTP/1.1" 200 -
{'message': 'Goodbye World!!'}
127.0.0.1 - - [14/Sep/2022 17:23:52] "POST /message HTTP/1.1" 200 -
{'message': 'In-the-middle World!!'}
127.0.0.1 - - [14/Sep/2022 17:23:52] "POST /message HTTP/1.1" 200 -
{'message': 'In-the-middle World!!'}
127.0.0.1 - - [14/Sep/2022 17:24:05] "POST /message HTTP/1.1" 200 -
{'message': 'Possibly World!!'}
127.0.0.1 - - [14/Sep/2022 17:24:05] "POST /message HTTP/1.1" 200 -
{'message': 'Possibly World!!'}

With the occasional solo POST on the first send.

I've looked into it, and this is usually because

  1. Something redirects, or errs, causing the code to be restarted, and activated twice. While I am completely new to HTTP, JS, PHP, etc. (the web languages), I cannot see that happening here
  2. A PreFlight operation happens, where the server sends a preliminary request to see if the main request can be sent ( question ). I looked into that, and I do not think that is the case, as the headers are identical in both requests:
>>> pprint.pprint(dict(h1))
{'Accept': '*/*',
 'Accept-Encoding': 'gzip, deflate, br',
 'Accept-Language': 'en-US,en;q=0.9',
 'Connection': 'keep-alive',
 'Content-Length': '18',
 'Content-Type': 'application/json',
 'Host': 'localhost:9000',
 'Origin': 'http://localhost:9000',
 'Referer': 'http://localhost:9000/',
 'Sec-Fetch-Dest': 'empty',
 'Sec-Fetch-Mode': 'cors',
 'Sec-Fetch-Site': 'same-origin',
 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
               '(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 '
               'Edg/105.0.1343.33',
 'sec-ch-ua': '"Microsoft Edge";v="105", " Not;A Brand";v="99", '
              '"Chromium";v="105"',
 'sec-ch-ua-mobile': '?0',
 'sec-ch-ua-platform': '"Windows"',
 'sec-gpc': '1'}
>>> pprint.pprint(dict(h2))
{'Accept': '*/*',
 'Accept-Encoding': 'gzip, deflate, br',
 'Accept-Language': 'en-US,en;q=0.9',
 'Connection': 'keep-alive',
 'Content-Length': '18',
 'Content-Type': 'application/json',
 'Host': 'localhost:9000',
 'Origin': 'http://localhost:9000',
 'Referer': 'http://localhost:9000/',
 'Sec-Fetch-Dest': 'empty',
 'Sec-Fetch-Mode': 'cors',
 'Sec-Fetch-Site': 'same-origin',
 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
               '(KHTML, like Gecko) Chrome/105.0.0.0 Safari/537.36 '
               'Edg/105.0.1343.33',
 'sec-ch-ua': '"Microsoft Edge";v="105", " Not;A Brand";v="99", '
              '"Chromium";v="105"',
 'sec-ch-ua-mobile': '?0',
 'sec-ch-ua-platform': '"Windows"',
 'sec-gpc': '1'}

And both contain the main message content

  1. The JavaScript is somehow being loaded twice, causing two functions to run simultaneously ( question ). As the JavaScript function I've written clears the input box immediately after sending it, and there is always some lag or latency, I doubt this is the case, but I don't know enough to disprove it

Regardless, I am now stuck. Everything I seem to try has no effect on cancelling this unpredictable (Sometimes the first message doesn't fire, sometimes it does) double POST, and cancelling it is mildly important to the messaging application I'm trying to shape this into. On top of using double the processing power, it is just incredibly inconvenient. Is there a way to stop this?

After doing some experimentation, I discovered that the problem resides in the browser itself.

Microsoft edge will always repeat the request unless you send back a response after the POST request. In this server code, all that is necessary is:

self.end_headers()

If this is your problem, open the console in Devtools (Inspect mode), and you should see something like this: 问题

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM