Here is my frontend JS. As a side note, FormData.append() does not work, so I have to set its properties explicitly. #data is an object with input name: value properties.
function http(data) {
const httpData = new FormData();
for (let prop in data) {
httpData[prop] = data[prop];
}
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4) {
const status = this.status;
const data = this.responseText;
}
};
xhttp.open('POST', '/api/login');
xhttp.setRequestHeader('Content-Type', 'x-www-form-urlencoded');
xhttp.send(httpData);
}
And on the server side
app.use(express.urlencoded({
extended: true
}));
express ready to parse form data, and in the /routes/api.js ( I am using express router )
router.post('/login', (req, res, next) => {
console.log(req.body);
res.end();
});
I got req.body as an {}
Where did I fuck up ?
Here is the working code, courtesy of #Quentin, frontend
function http(data) {
const httpData = new FormData();
for (let prop in data) {
httpData.append(prop, data[prop]);
}
const xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4) {
const status = this.status;
const data = this.responseText;
}
};
xhttp.open('POST', '/api/login');
xhttp.send(httpData);
}
and backend in /routes/api.js
const formidable = require('formidable');
router.post('/login', (req, res, next) => {
const form = formidable({ multiples: true });
form.parse(req, (err, fields, files) => {
console.log(fields);
res.end();
});
});
You have three problems.
append() does work. It just doesn't store the data in a way that shows up with console.log. Assigning arbitrary properties doesn't work.
You need to send the correct Content-Type header. This is multipart/form-data with a mandatory boundary parameter that you have no way of knowing. Don't try to set the Content-Type header manually. XMLHttpRequest will generate it automatically from the FormData object.
You need a body parser for Express that supports multipart/form-data. The built-in one does not. The documentation for it on npm suggests 4 alternatives.
Related
I have this problem because I am sending data via JavaScript using XMLHttpRequest but not receiving it in node.js express post method. I don't know what I'm doing wrong.
My script - funcion form execute on window load
function form() {
const btn = document.getElementById("save-data");
let currentPage = window.location.href.split("/").at(-1);
function sendData(data) {
const XHR = new XMLHttpRequest();
XHR.open("POST", window.location.href);
XHR.setRequestHeader("Content-Type", "application/json");
console.log(data); // I get right data
XHR.send(data);
}
btn.addEventListener("click", () => {
switch (currentPage) {
case "settings":
const prefix = document.getElementById("prefix").value;
let buttonState = document.getElementById("module-state");
if (buttonState.hasAttribute("enable")) {
buttonState = "enable";
} else {
buttonState = "disable";
}
sendData({
prefix: prefix,
buttonState: buttonState,
announcements: {
giveawayChannelID: 123,
updateChannelID: 123,
},
});
}
});
}
My Node.js code
router.post('/:id/settings', (req, res) => {
console.log(req.body) // {}
})
Pleas help.
You need to install body-parser package by NPM, and require it in your code
Try
npm i body-parser
Then in your main node script, require it
const bodyParser = require('body-parser');
after that, you should set it as a middleware for express
app.use(bodyParser.json()); //Handles JSON requests
app.use(bodyParser.urlencoded({ extended: false })); //Handles normal post requests
after these steps, you can access values like req.body.value
How to encode a request body using HMAC sha 256 and base64.
The request object that i receives from xero webhook.
HEADER:
"x-xero-signature" : HASH_VALUE
PAYLOAD:
{
"events": [],
"lastEventSequence": 0,
"firstEventSequence": 0,
"entropy": "S0m3r4Nd0mt3xt"
}
The note from xero documentation says "If the payload is hashed using HMACSHA256 with your webhook signing key and base64 encoded, it should match the signature in the header. This is a correctly signed payload. If the signature does not match the hashed payload it is an incorrectly signed payload."
And I followed this example : https://devblog.xero.com/using-xero-webhooks-with-node-express-hapi-examples-7c607b423379
const express = require("express");
const router = express.Router();
const base64 = require('base-64');
const crypto = require('crypto')
const bodyParser = require('body-parser')
const xero_webhook_key = '00fRRlJBYiYN4ZGjmTtG+g/pulyb1Eru68YYL3PFoLsa78dadfQtGrOMuISuVBHxpXeEYo0Yy1Gc+hHMhDkSI/EEcgtrA==';
let options = {
type: 'application/json'
};
let itrBodyParser = bodyParser.raw(options);
router.post("/", itrBodyParser, async (req, res, next) =>{
// console.log('::::WebhookPost:::');
const reSign = req.headers['x-xero-signature'];
console.log(req.headers['x-xero-signature']);
console.log('::::::::');
console.log(req.body);
console.log("Body: "+JSON.stringify(req.body))
console.log(req.body.toString());
console.log("Xero Signature: "+ reSign);
console.log('Server key::::',xero_webhook_key);
// Create our HMAC hash of the body, using our webhooks key
let hmac = crypto.createHmac("sha256", xero_webhook_key).update(req.body.toString()).digest('base64');
console.log("Resp Signature: ",hmac)
if (req.headers['x-xero-signature'] == hmac) {
res.statusCode = 200
} else {
res.statusCode = 401
}
console.log("Response Code: "+res.statusCode)
return res.send();
});
Hey I recently did a video on implementing webhooks with Xero, let me know if this gets you unstuck. I found that trying to pass itrBodyParser on the route the way you have wasn't working for me so I switched it with an app.use statement on my specific webhooks endpoint. If you prefer a written guide over video, here's the blog post
I solved it using this solution.! I was using express framework and the request were not getting as raw request also .toString of didn't worked as mentioned in xero documentation.
const server = http.createServer(async (req, resp) => {
try {
console.log(`::::Webhook::: ${webhookPort}`);
console.log("::::x-xero-signature:::");
console.log(req.headers["x-xero-signature"]);
console.log(`--------------------------------------`);
if (req.method === "POST") {
if(req.headers["x-xero-signature"]){
const rData = await new Promise((resolve, reject) => {
return collectRequestData(req, (result) => {
console.log(result);
let hmac = crypto
.createHmac("sha256", xero_webhook_key)
.update(result)
.digest("base64");
console.log("Resp Signature: ", hmac);
return resolve({
hmac,result
});
});
});
console.log(">>Resp Signature: ", rData);
console.log('>>x-xero-signature:::',req.headers["x-xero-signature"]);
if(rData.result){
const result = JSON.parse(rData.result);
console.log('result:::',result);
for(let { resourceId } of result.events) {
console.log('::INVOICE ID = ',resourceId);
getInvoiceData(resourceId);
}
}
if(rData.hmac == req.headers["x-xero-signature"] ){
console.log('::YES');
resp.statusCode = 200;
}else{
console.log('::NO');
resp.statusCode = 401;
}
}
resp.end();
}
console.log("::::Webhookgetsssss:::");
resp.message = 'Get API'
resp.end();
} catch (error) {
resp.statusCode = 200;
resp.end();
}
});
server.listen(webhookPort);
function collectRequestData(request, callback) {
let body = "";
request.on("data", (chunk) => {
body += chunk.toString();
});
request.on("end", () => {
callback(body);
});
}
I'm new to Express and trying to create a login authentication system. But I'm having issues getting XHR to show a ready state 4. It was working fine until I added express.Router which I saw in a GitHub project.
These are my relevant bits of code:
// server.js
const userAuth = require("./auth");
app.use(cors());
app.use(bodyParser.json());
app.use("/auth", userAuth);
// ./auth/index.js
const router = require("express").Router();
const userLogin = require("./userLogin");
const userRegister = require("./userRegister");
router.post("/login", userLogin);
router.post("/register", userRegister);
module.exports = router;
// ./auth/userLogin.js
module.exports = (req, res) => {
const { username, password } = req.body;
console.log(username, password);
res.status(200).send("hello");
};
// client side
const http = new XMLHttpRequest();
http.open("post", "https://example.com/auth/login");
http.setRequestHeader("Content-type", "application/json");
http.onreadystatechange = () => {
if (!(http.readyState === 4 && http.status === 200)) {
console.error("Could not submit form.");
console.log(http.responseText);
}
};
http.send(JSON.stringify({ username, password }));
The chrome console shows "State 2", then "State 3" plus the "hello" response. But there is no "State 4"?
The code is pretty much identical to the linked Github project (theirs have async in userLogin.js but I've tried that too).
Any help/tips much appreciated!
change your onreadystatechange method to this and it will work:
http.onreadystatechange = function () {
// rest of your code
}
This is a template you can refer back to if needed:
const http = new XMLHttpRequest();
http.open("post", "https://example.com/auth/login");
http.setRequestHeader("Content-type", "application/json");
http.onreadystatechange = function () { // change this line
if (http.readyState === 4 && http.status === 200) {
console.log(http.responseText);
}
else {
console.error("Error in submitting or receiving response.");
}
};
http.send(JSON.stringify({ username, password }));
if this answer helps solving the issue, consider accepting the answer or upvoting it. Thank you.
I have a Geojson file which name is countries.geojson.
I can read this file with get method. I want to create PBF files from this GeoJSON file. How can I do that?
var express = require("express");
const fs = require('fs');
var Pbf = require('pbf');
var protobuf = require('protocol-buffers')
const path = require('path');
var app = express();
app.get("/get/data", (req, res, next) => {
fs.readFile('data/countries.geojson', (err, data) => {
if (err) throw err;
let country = JSON.parse(data);
res.send(country);
});
});
app.get("/", (req, res, next) => {
// Create PBF file
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
To encode, use the example provided by Horatio Jeflea in that answer.
To decode, as in using the response in a web browser, use something like this or (more modernly, the fetch lib).
var xmlhttp = new XMLHttpRequest();
// Get the encoded geobuf file
xmlhttp.open("GET", "http://<somedomain>/uscty.pbf", true);
xmlhttp.responseType = "arraybuffer"; // important to know what the data type is
xmlhttp.onload = function () {
// convert the raw response to a pbf
var pbf = new Pbf(new Uint8Array(xmlhttp.response));
// use geobuf lib to decode it to GeoJSON
var decoded = geobuf.decode(pbf);
console.log("Decoded GeoJSON", decoded);
};
xmlhttp.send();
Use geobuf library:
var buffer = geobuf.encode(geojson, new Pbf());
I'm attempting to post a JSON object to my server, but when I console log req.body it shows an empty object. Here's my code:
var submitRecipe = () => {
let recipe = {alias: null, description: null, instructions: null};
recipe.alias = document.getElementById('alias').value;
recipe.description = document.getElementById('description').value;
recipe.instruction = document.getElementById('instructions').value;
postRequest("/createrecipe", recipe, (xhr) => {
console.log("Hello!" + JSON.parse(xhr.responseText));
})
};
var postRequest = (url, data, callback = undefined) => {
xhr.onreadystatechange = () => {
//Call a function when the state changes.
if(xhr.readyState == 4 && xhr.status == 200) {
console.log("testing");
return callback(200 , xhr.responseText);
}else{
return callback(400, xhr.responseText);
}
}
xhr.open('POST', url)
xhr.send(data);
}
Node
createRecipe = function(req, res){
console.log(req.body);
}
I'm using express to transfer the server information, and I am using bodyParser.json(). From there, I just call the controller with the following:
express
var express = require("express");
var bodyParser = require("body-parser");
var server = express();
var recipeController = require("./controllers/recipeController");
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true}));
server.post("/createrecipe", recipeController.createRecipe);
The createRecipe function just console logs the information, but as stated before, req.body is an empty object. All tips are appreciated.
XHR expects your data to be encoded or packed in whatever way you expect it to be send unlike other library wrappers like jQuery or Angular Ajax wrapper functions.
Alsobody-parsermiddleware was not identifying the Content-type and was not activating for the required request.
Simply JSON.stringify your json data
data = JSON.stringify(data);
and add the application/json MIME type as xhr's content-type header.
xhr.setRequestHeader("content-type", "application/json");
Also, If you want to use url-encoded do the encoding of your data before attaching and add the corresponding header content type.
My full test code (for reference purposes):
Server (testServer.js):
var express = require("express");
var bodyParser = require("body-parser");
var server = express();
server.use(bodyParser.json());
server.use(bodyParser.urlencoded({ extended: true}));
server.post("/createrecipe", function(req, res){
console.log(req.body);
var resp = {server: "hello world", dataReceived: req.body};
res.json(resp);
});
server.get("/", function(req, res){
res.sendFile(__dirname + "/testClient.html");
})
server.listen(3000, function(){
console.log("Server running");
})
Client (testClient.html):
<input type="text" id="alias" value="a">
<input type="text" id="description" value="b">
<input type="text" id="instructions" value="c">
<button onclick="submitRecipe()"> TEST</button>
<script>
var submitRecipe = () => {
let recipe = {alias: null, description: null, instructions: null};
recipe.alias = document.getElementById('alias').value;
recipe.description = document.getElementById('description').value;
recipe.instructions = document.getElementById('instructions').value;
postRequest("/createrecipe", recipe, (status, xhr) => {
var data = (JSON.parse(xhr.responseText));
console.log(data.dataReceived);
})
};
var postRequest = (url, dataObj, callback = undefined) => {
//--------------Added line--------------------
var data = JSON.stringify(dataObj);
//--------------Added line--------------------
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
//Call a function when the state changes.
if(xhr.readyState == 4 && xhr.status == 200) {
return callback(200 , xhr);
}else if(xhr.status == 400){
return callback(400, xhr);
}
}
xhr.open('POST', url)
//--------------Added line--------------------
xhr.setRequestHeader("content-type", "application/json");
//--------------Added line--------------------
xhr.send(data);
}
</script>