0

Here is a server-side function that receives a POST request and handles its response. As you can see, when no error occurs, the status code of the response is 200.

app.post('/submit', function(req, res) {
    var p = new Promise(function(resolve, reject) {
        db.serialize(function() {
            db.run("INSERT INTO users VALUES (?, ?, ?, ?)",
            [req.query['email'], req.query['company'], req.query['subject'], req.query['text']],
            function (err) {
                if (err) {
                    console.error(err);
                    reject();
                } else {
                    console.log("Transaction passed");
                    resolve();
                }
            });
        });
    });
    p.then(function(){
        res.status(200).end();
    }).catch(function() {
        res.status(400).end();
    })
});

In the client side, I wrote a JS function that sends an AJAX POST request to the server and handles the response:

function addFormToDB(email, company, subject, text) { 
    var xhttp = new XMLHttpRequest();
    var params = "email=" + email + "&company=" + company + "&subject=" + subject + "&text=" + text;
    xhttp.open("POST", "http://127.0.0.1:3000/submit?" + params, true);
    xhttp.onreadystatechange = function() {
        console.log(xhttp.readyState + " " + xhttp.status);
        if (xhttp.readyState == 4 && xhttp.status == 200) {
            console.log("request " + params + " was sent to DB");
            alert("Thank You!");
        }
    };
    xhttp.send();
}

When I invoke the function 'addFormToDB' from the console window of the browser, it sends an AJAX request to the server and gets it back with an xhhr.status of 200.

However, 'addFormToDB' has to be invoked when the user fills a form and clicks the 'submit' button. When this happens, the xhhr.status sent back from the server to the browser is always 0. I don't know why.

Here is the HTML code that creates the form:

<form id="form">
        <label for="email"> E-mail: </label>
        <input id = "email"> <br>
        <label for="company"> Company: </label>
        <input id = "company"> <br>
        <label for="subject"> Subject: </label>
        <input id = "subject"> <br>
        <label for="text"> Text: </label>
        <input id = "text"> <br>
        <input type="submit" value="Submit" id="button"><br><br>
    </form>
...
<script>
$("#form").submit(function() {
    addFormToDB($("#email").val(), $("#company").val(), $("#subject").val(), $("#text").val())
});
</script>

Can you help me find out the bug?

CrazySynthax
  • 9,442
  • 21
  • 70
  • 136
  • 1
    I can't reproduce the problem: http://i.imgur.com/27bhIwa.png – Quentin Dec 27 '16 at 00:55
  • OK. What invokes the function addFormToDB is a button click. I have an html form with a button, once the button is clicked, inputs of the form are read and then the function addFormToDB is called. Probably the problem is with the button click.... Do you have any idea? – CrazySynthax Dec 27 '16 at 01:00
  • `xhttp.status is always 0` doesn't it means it can't connect to the host? [related, http status code 0](http://stackoverflow.com/questions/2000609/jquery-ajax-status-code-0) – Bagus Tesa Dec 27 '16 at 01:07
  • `the status xhttp.status is always 0`- could also be CORS - the fact that the request is to `http://127.0.0.1:3000/` suggests it's to a different host than the page is served from – Jaromanda X Dec 27 '16 at 01:35
  • You have to `send(params)` not `open(url + params)` – Kaiido Dec 27 '16 at 09:04
  • But, how will the server know that the request is for '/sumbit'? It works without the button. It doesn't make sense that this is the solution. – CrazySynthax Dec 27 '16 at 09:08
  • `xhr.open('POST', 'http://127.0.0.1:3000/submit'); ... xhr.send(params);` + you could simplify everything by just doing `xhr.open('POST', 'http://127.0.0.1:3000/submit'); xhr.send(new Formdata(document.getElementById('form'));`. – Kaiido Dec 27 '16 at 09:22
  • I tried it. It didn't help :( – CrazySynthax Dec 27 '16 at 09:26
  • 1
    You currently do not properly encode your request parameters. You can't simply build a query string with string concatenation. You *must* call `encodeURIComponent()` on every value you want to transfer. Either that, or use a Ajax library that abstracts away the busywork (jQuery Ajax, reqwest, just pick one). Solve this problem first before you do anything else. – Tomalak Dec 27 '16 at 09:29
  • But why does it happen specifically when I invoke the function by pressing the 'submit' button? – CrazySynthax Dec 27 '16 at 09:37
  • 2
    Do you prevent traditional form transmit (the one where the page reloads) in your submit handler? My guess is that you don't. – Tomalak Dec 27 '16 at 09:45
  • Try adding `return false;` at the end of your `submit` event listener. – jcaron Dec 27 '16 at 09:52
  • @jcaron, Hallelujah! It works! If you want, write an answer to this post, and I promise I'll accept it. – CrazySynthax Dec 27 '16 at 09:57
  • Credit should go to @Tomalak as he pointed out the actual issue first. – jcaron Dec 27 '16 at 09:58
  • OK, guys, decide who writes the answer and I'll accept it (so he'll get the credit). – CrazySynthax Dec 27 '16 at 10:01
  • Also look at the extension I wrote to my answer. – Tomalak Dec 27 '16 at 11:21

1 Answers1

1

The immediate issue is that you don't prevent the default action of a form submit - which is to POST data to the server and reload the page. As a side-effect this cancels all running Ajax requests, which causes the effect you see.

To prevent the default event action, call preventDefault() on the event object.

function submitHandler(e) {
    e.preventDefault();
    // some processing, for example Ajax
    // the browser will not issue a traditional POST request
}

Back in the day, return false; had the same effect, but nowadays.preventDefault() is the way to go.


Further comments on your code:

Client Side

Don't roll your own Ajax functions. Ajax libraries are plenty and convenient, well-tested and provide easy-to-read Ajax support that prevents common errors and does all kinds of heavy lifting transparently. Just use one of the many libraries.

With jQuery, which you seem to be using anyway, the function becomes as straight-forward as this:

function addFormToDB(email, company, subject, text) {
    return $.post("http://127.0.0.1:3000/submit", {
            email: email,
            company: company,
            subject: subject,
            text: text
        })
        .done(function (data) {
            console.log("received", data);
            alert("Thank You!");
        })
        .fail(function (jqXhr, status, err) {
            console.error(err);
        });
}

Server Side

Don't do your own promisification. It might look easy enough, but just as with Ajax, there's enough stuff that can go wrong and is easily overlooked. Let a library do it for you, or use a wrapper library that did it for you. For node-sqlite3, one such wrapper library exists: co-sqlite3. I recommend you look at it.

Your code could look like this:

app.post('/submit', function(req, res) {
    var q = req.query;
    var params = [q.email, q.company, q.subject, q.text];

    db.serialize()
        .then(() => db.run("INSERT INTO users VALUES (?, ?, ?, ?)", params))
        .then(() => res.status(200).end())
        .catch((err) => {
            console.error(err);
            res.status(400).end();
        });
    });
});

Or use one of the async/yield variants shown in the library's samples.

Tomalak
  • 306,836
  • 62
  • 485
  • 598