In the below code when I try to hit the "/hello" route using the get method I am getting the following response. All the other routes are working fine except the "/hello" route using get method. Why is this happening?
var http = require('http');
var url = require('url');
var stringDecoder = require('string_decoder').StringDecoder;
var server = http.createServer(function(req,res){
//parse the URL and get the path if we pass true as paramater it will act as query string
var parsedURL = url.parse(req.url,true);
//Get the path alone
var path = parsedURL.pathname;
//Trimm the URL
var trimmedpath = path.replace(/^\/+|\/+$/g,'');
//Get the method
var method = req.method.toLowerCase();
//Get the headers
var headers = req.headers;
//get the querystring as an object
var queryStringObject = parsedURL.query;
//Get the payload if any
var decoder = new stringDecoder('utf-8');
var buffer = '';
req.on('data',function(data){
buffer += decoder.write(data);
});
req.on('end',function(){
buffer += decoder.end();
//Generate the data
var data = {
'method' : method,
'headers' : headers,
'trimmedpath' : trimmedpath,
'queryStringObject' : queryStringObject
}
var Handler = routes[trimmedpath];
var chosenHandler = typeof(Handler) !== 'undefined' ? Handler : handlers.notfound;
chosenHandler(data,function(statuscode,payload){
statuscode = typeof(statuscode) == 'number' ? statuscode : 300;
payload = typeof(payload) == 'object' ? payload : {};
console.log(payload);
var payloadString = JSON.stringify(payload);
//set the content type to view as object
res.setHeader('Content-Type','application/json');
res.writeHead(statuscode);
res.end(payloadString);
});
//Sending the response
console.log(buffer);
console.log(Handler,chosenHandler);
res.end("hello world");
});
});
var handlers = {};
handlers.hello = function(data,callback){
if(data.method == 'post'){
callback(404,{'Message' : 'You have hitted the post route'});
}
if(data.method == 'get'){
callback(143,{'message': 'you have hitted the get route'});
}
}
handlers.ping = function(data,callback){
callback(999,{'message':'ping route'});
}
handlers.notfound = function(data,callback){
callback(600,{'Message' : 'Major issue'});
}
var routes = {
'ping' : handlers.ping,
'hello' : handlers.hello
}
server.listen(3000,function(){
console.log("server started listening in the port 3000");
});
I have split the messages based on the method received from the request.
It looks like you're comparing HTTP method in small
handlers.hello = function(data,callback){
if(data.method === 'POST'){
callback(404,{'Message' : 'You have hitted the post route'});
}
if(data.method === 'GET'){
callback(143,{'message': 'you have hitted the get route'});
}
}
I'll suggest you to use some already built router to handle multiple cases without increasing code length.
Update
considering you're already lower casing http method, I can see hello is being called without modifying your code;
{ message: 'you have hitted the get route' }
[Function] [Function]
{ Message: 'You have hitted the post route' }
[Function] [Function]
And the response in POSTMAN.
Related
I'm having issue with site. I need to use XMLHttpRequest to send GET request to my api running on Node.js
The Issue is that when I do the request it doesn't send the payload. It's just an empty object. If I do the same without the XMLHttpRequest, for example in Postman everything works.
This is my XMLHttpRequest Function:
// AJAX Client (for RESTful API)
app.client = {}
// Interface for making API calls
app.client.request = function(headers,path,method,queryStringObject,payload,callback){
// Set defaults
headers = typeof(headers) == 'object' && headers !== null ? headers : {};
path = typeof(path) == 'string' ? path : '/';
method = typeof(method) == 'string' && ['POST','GET','PUT','DELETE'].indexOf(method.toUpperCase()) > -1 ? method.toUpperCase() : 'GET';
queryStringObject = typeof(queryStringObject) == 'object' && queryStringObject !== null ? queryStringObject : {};
payload = typeof(payload) == 'object' && payload !== null ? payload : {};
callback = typeof(callback) == 'function' ? callback : false;
// For each query string parameter sent, add it to the path
var requestUrl = path+'?';
var counter = 0;
for(var queryKey in queryStringObject){
if(queryStringObject.hasOwnProperty(queryKey)){
counter++;
// If at least one query string parameter has already been added, preprend new ones with an ampersand
if(counter > 1){
requestUrl+='&';
}
// Add the key and value
requestUrl+=queryKey+'='+queryStringObject[queryKey];
}
}
// Form the http request as a JSON type
var xhr = new XMLHttpRequest();
xhr.open(method, requestUrl, true);
xhr.setRequestHeader("Content-type", "application/json");
// For each header sent, add it to the request
for(var headerKey in headers){
if(headers.hasOwnProperty(headerKey)){
xhr.setRequestHeader(headerKey, headers[headerKey]);
}
}
// If there is a current session token set, add that as a header
if(app.config.sessionToken){
xhr.setRequestHeader("token", app.config.sessionToken.id);
}
// When the request comes back, handle the response
xhr.onreadystatechange = function() {
if(xhr.readyState == XMLHttpRequest.DONE) {
var statusCode = xhr.status;
var responseReturned = xhr.responseText;
// Callback if requested
if(callback){
try{
var parsedResponse = JSON.parse(responseReturned);
callback(statusCode,parsedResponse);
} catch(e){
callback(statusCode,false);
}
}
}
}
// Send the payload as JSON
var payloadString = JSON.stringify(payload);
xhr.send(payloadString);
};
This is code from when I make the request:
app.loadCartViewPage = function(){
var emailAdress = typeof(app.config.sessionToken.emailAdress) == 'string' ? app.config.sessionToken.emailAdress : false;
if(emailAdress){
var queryStringObject = { 'emailAdress' : emailAdress };
app.client.request(undefined, 'api/users', 'GET', queryStringObject, undefined, function(statusCode, responsePayload){
if(statusCode == 200){
var cartId = typeof(responsePayload.carts) == 'object' && responsePayload.carts instanceof Array ? responsePayload.carts[0] : [];
var payload = {'emailAdress' : emailAdress, 'cartId' : cartId };
app.client.request(undefined, 'api/carts', 'GET', undefined, payload, function(statusCode, responsePayload){
console.log(responsePayload);
});
} else {
app.logUserOut();
}
});
} else {
app.logUserOut();
}
};
The first GET request goes through as it should where I pass only queryString, but the second sends just empty objects.
Thanks for help in advance
XMLHttpRequest does not send a body for a GET or HEAD request. Arguments or options for those requests should be in query parameters, not in the body.
Here's a note from the MDN page on .send():
send() accepts an optional parameter which lets you specify the request's body; this is primarily used for requests such as PUT. If the request method is GET or HEAD, the body parameter is ignored and the request body is set to null.
I’m attempting to authenticate to Coinbase Pro from a Google Script. I’ve managed to do this already in Postman using CryptoJS, but I’m running into issues generating the CB-ACCESS-SIGN signature header. I’ve set up a test using a test key and secret string to figure out the differences between CryptoJS.HmacSHA256 and Utilities.computeHmacSha256Signature, the implementation Google offers, and noticed a difference in parameters: CryptoJS.HmacSHA256 expects a secret as WordArray while Utilities.computeHmacSha256Signature expects a secret as string.
In Postman I'm doing the following to get the wordarray of my apiSecret to pass to CryptoJS.HmacSHA256:
var hash = CryptoJS.enc.Base64.parse(pm.variables.get('apiSecret'));
In my Google script I'm doing the sam
var hash = Utilities.base64Decode(apiSecretB64)
I've tried debugging this with the same secret and message, but I'm getting different results.
My implementation in Postman:
function computeSignature(request) {
const data = request.data;
const method = request.method.toUpperCase();
const path = getPath(request.url);
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
const apiSecret = CryptoJS.enc.Base64.parse(pm.variables.get('apiSecret'));
const hash = CryptoJS.HmacSHA256(message, apiSecret);
const hashInBase64 = CryptoJS.enc.Base64.stringify(hash);
return hashInBase64;
}
And my implementation in Google Script:
function computeSignature(request, path, timestamp) {
const data = request.data;
const method = request.method;
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
var apiSecret = Utilities.base64Decode(apiSecretB64);
var hash = Utilities.computeHmacSha256Signature(message, apiSecret);
hash = Utilities.base64Encode(hash);
return hash;
}
Does anyone know why I'm getting different results?
I've managed to solve the issue by converting the message from string to a byte array:
function computeSignature(request, path, timestamp) {
const data = request.data;
const method = request.method;
const body = (method === 'GET' || !data) ? '' : JSON.stringify(data);
const message = timestamp + method + path + body;
var apiSecretByteArr = Utilities.base64Decode(apiSecretB64);
var messageByteArr = Utilities.base64Decode(Utilities.base64Encode(message));
var hash = Utilities.computeHmacSha256Signature(messageByteArr, apiSecretByteArr);
return Utilities.base64Encode(hash);
}
There is probably a better way of doing this, but at least the correct signature is now being computed.
I have an incoming Post request in JSON format like this:
[{"username":"ali","hair_color":"brown","height":1.2},{"username":"marc","hair_color":"blue","height":1.4},{"username":"zehua","hair_color":"black","height":1.8}]
and I want to change it into this format, and then send it to a RESTFUL API:
{"h":["username","hair_color","height"],"d":[["ali","brown",1.2],["marc","blue",1.4],["zehua","black",1.8]]}
The question is How to change the JSON format regardless different parameter names, "h" is for the parameter names and "d" is for the values?
Below is my js code:
var express = require("express");
var myParser = require("body-parser");
var app = express();
app.use(myParser.urlencoded({extended : true}));
app.use(myParser.json());
app.post("/parseJSON", function(request, response) {
console.log(request.body); //This prints the JSON document received (if it is a JSON document)
if(request.body.constructor === Object && Object.keys(request.body).length === 0)
{
response.end("-1");
}
else
{
response.end("0");
}
});
app.get("/parseJSON", function(request, response) {
console.log(request.body); //This prints the JSON document received (if it is a JSON document)
if(request.body.constructor === Object && Object.keys(request.body).length === 0)
{
response.end("-1");
}
else
{
response.end("0");
}
});
app.listen(8080);
can you try it?
var key = Object.keys(request.body[0])
var data = request.body.map(ele => {
return Object.values(ele);
})
var result = {h:key,d:data};
or
const h = Object.keys(request.body[0])
const d = request.body.map(ele => Object.values(ele)) // Use ES7 to enable using Object.values()
const result = {h, d};
when I'm trying to set the header in the following way ,it's working absolutely fine.
response.setHeader('Content-Type', 'application/json');
but when I'm trying to add variable instead of exact header name/value in the following way, it's showing error :-(
response.setHeader(result.headers);
if you console.log("headers -> " + result.header) the result would be the same.
headers -> 'Content-Type', 'application/json'
following are the exact error I'm getting , not able to figure out how to get around it.
_http_outgoing.js:487
throw new TypeError(`Header name must be a valid HTTP Token ["${name}"]`);
^
TypeError: Header name must be a valid HTTP Token ["'Content-Type', 'application/json'"]
at validateHeader (_http_outgoing.js:487:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at C:\service-mocker\src\main\app.js:54:22
at FSReqWrap.readFileAfterClose [as oncomplete] (fs.js:511:3)
Below is whole code I'm trying to implement :
var http = require('http');
var fs = require('fs');
var path = require('path');
var repl = require('repl');
var map={};
var key;
var value;
//create a server object:
var server = http.createServer(function(request, response) {
fs.readFile("./resources/FileData1.txt", function(err, data) {
if(err) throw err;
content = data.toString().split(/(?:\r\n|\r|\n)/g).map(function(line){
return line.trim();
}).filter(Boolean)
var result = processFile(content);
console.log("url -> " + result.url);
console.log("status -> " + result.status);
console.log("headers -> " + result.headers);
console.log("body -> " + result.body);
function objToString (obj) {
var str = '';
for (var p in obj) {
if (obj.hasOwnProperty(p)) {
str += obj[p] + '\n';
}
}
return str;
}
function processFile(nodes) {
nodes.forEach(function(node) {
if(node.startsWith("//")){
key = node.substring(2, node.length-2).toLowerCase().trim();
return;
}
else{
value = node;
}
// map[key] = value;
if(key in map){
map[key].push(value);
}else{
map[key]= [value];
}
});
return map;
// console.log(map);
}
if(request.url == result.url ){
response.setHeader(result.headers);
// response.setHeader('Content-Type', 'application/json');
response.write( objToString(result.body) );
response.statuscode = parseInt( result.status );
response.end();
}else {
// response.writeHead(404, {"Content-Type": "text/html"});
response.end("No Page Found");
}
});
});
var port = process.env.PORT || 8080;
server.listen(port, function() {
console.log('Listening on port ' + port);
});
It looks like result.headers is returning a single string, but response.setHeader needs two arguments: the header name and the header value.
Based on the error message, you're doing the equivalent of:
response.setHeader("'Content-Type', 'application/json'");
Instead, you need to split out the header into key and value:
//n.b. make sure that your result only has the one header!
var headerSplit = result.headers.split(',');
var headerKey = headerSplit[0];
var headerVal = headerSplit[1];
response.setHeader(headerKey, headerVal);
If you dont want to set headers one at a time with response.setHeader, you should use response.writeHead, which will write the status code and your headers into the response.
response.writeHead(200, headers)
Here headers must be a JavaScript object.
Replace
response.setHeader(result.headers);
response.statuscode = parseInt( result.status );
with
response.writeHead(parseInt(result.status), parseHeaders(result.headers));
where parseHeaders is a function that parses result.headers into an object.
function parseHeaders(headers) {
var o = {};
headers.forEach(header => {o[header.split(',')[0]] = header.split(',')[1]});
return o;
}
In this answer I've assumed result.headers is formatted like so
[
"header1, header1value",
"header2, header2value"
]
Template literals use a back-tick not quotes.
I am using Node JS HTTP request. When my response length exceeds 16101 , it truncates my response. And I recieve limited response like this:
{"id_user":"133696"},{"id_u
This is not a chunked response, its only comes once. I want to recieve the whole response instead of truncated .
My Node version is v0.10.36.
Here is my code:
var https = require('https');
var querystring = require('querystring');
postData.format = 'json';
postData.apikey = 'abcd';
jsonObject = querystring.stringify(postData);
var postheaders = {
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length' : Buffer.byteLength(jsonObject, 'utf8')
};
if(callMethod == undefined){
callMethod = 'POST';
}
var optionspost = {
host : this.host,
port : this.port,
path : this.path,
method : callMethod,
headers : postheaders
};
var reqPost = https.request(optionspost, function(res) {
res.setEncoding('utf-8');
res.on('data', function(responseData) {
//---->>> responseData containes truncated response
if(callFunction != undefined && callFunction != null && callFunction != ''){
callFunction(responseData, relatedData);//****** calling success function ****
}
});
res.on('end', function() {
});
});
reqPost.write(jsonObject);
reqPost.end();
reqPost.on('error', function(e) {
console.error(e);
});
Your code is expecting the data event only once, but Node can fire it more than once. In fact, it can fire it as many times as it damn pleases.:) Every time a data event is emitted, another part of the data is provided to you. You know that there is no more data to be consumed when the end event is fired - that's where you should process the data and/or call your callbacks.
Since the response is basically a readable stream, have a look at the data event for Readable Stream.