I found a very useful code snippet on GitHub that can provide simple server-client communication in NodeJS.
After some minor formatting, my code looks like this:
The client (Jade + Javascript)
head
title jsonp test
script(src='http://code.jquery.com/jquery-1.6.2.min.js')
script(type='text/javascript').
$(function () {
$('#select_link').click(function (e) {
e.preventDefault();
console.log('select_link clicked');
var data = {};
data.title = "title";
data.message = "message";
$.ajax({
type: 'POST',
data: JSON.stringify(data),
contentType: 'application/json',
url: 'http://localhost:7776/domaintest',
success: function (data) {
console.log('success');
console.log(JSON.stringify(data));
}
});
});
});
body
#select_div
a#select_link(href='#') Test
The server (Javascript)
var express = require('express');
var app = express.createServer();
app.use(express.bodyParser());
app.post('/domaintest', function(req, res){
var obj = {};
console.log('body: ' + JSON.stringify(req.body));
res.send(req.body);
});
app.listen(7776);
Route defined to the client (I was told it's unnecessary for the server as app.post serves the purpose)
var express = require('express');
var router = express.Router();
router.get('/', function(req, res, next) {
res.render('index');
});
module.exports = router;
The result is a simple text reading "Test" that can be clickable. When I click, the actual events should happen as far as I read it out from the code, but insted browser says POST http://localhost:7776/domaintest 404 (Not Found) in jquery-1.6.2.min.js:18. To be very precise, the error occures in $.ajax, according to the debugger.
Since the jQuery code is practically unreadable because of the formatting (not judging, it might have its reason), I need your help. What's the possible source of the error? Did I forget mentioning something?
Change your server.js file to this. I havent tested the code but this should work.
var express = require('express');
var app = express.createServer();
var router = express.Router();
app.use(express.bodyParser());
router.post('/domaintest', function(req, res, next) {
var obj = {};
console.log('body: ' + JSON.stringify(req.body));
res.send(req.body);
});
app.listen(7776);
You can read this for more information http://adrianmejia.com/blog/2014/10/01/creating-a-restful-api-tutorial-with-nodejs-and-mongodb/
I think this is because your calling body-parser incorrectly. Body parser isn't part of express so you can't do express.bodyParser.
You need to include it like so:
var bodyParser = require('body-parser);
app.use(bodyParser.urlencoded({ extended: false }))
After some research and outside help, I've found the solution.
For domaintest, there was a route defined, which is almost identical to the one I defined for the client. Well, to the domaintest route, I had to add the next function call, right after router.get:
router.post('/', function(req, res) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify({data: 'asd'}));
});
The so-called server code above is practically unused and unnecessary; domaintest serves as a server.
Related
I am using nodejs + Express (v3) like this:
app.use(express.bodyParser());
app.route('/some/route', function(req, res) {
var text = req.body; // I expect text to be a string but it is a JSON
});
I checked the request headers and the content-type is missing. Even if "Content-Type" is "text/plain" it is parsing as a JSON it seems. Is there anyway to tell the middleware to always parse the body as a plain text string instead of json? Earlier versions of req used to have req.rawBody that would get around this issue but now it does not anymore. What is the easiest way to force parse body as plain text/string in Express?
By default bodyParser.text() handles only text/plain. Change the type options to include */json or */*.
app.use('/some/route', bodyParser.text({type: '*/*'}), function(req, res) {
var text = req.body; // I expect text to be a string but it is a JSON
});
//or more generally:
app.use(bodyParser.text({type:"*/*"}));
You can find the docs here
In express 4.x you can use the text parser from bodyParser
https://www.npmjs.org/package/body-parser
just add in app.js
app.use(bodyParser.text());
Also in the desired route
router.all('/',function(req,res){
console.log(req.body);
})
If you remove the use of the bodyParser() middleware, it should be text. You can view the bodyParser docs for more info: http://www.senchalabs.org/connect/middleware-bodyParser.html
Remove this line:
app.use(express.bodyParser());
EDIT:
Looks like you're right. You can create your own rawBody middleware in the meantime. However, you still need to disable the bodyParser(). Note: req.body will still be undefined.
Here is a demo:
app.js
var express = require('express')
, http = require('http')
, path = require('path')
, util = require('util');
var app = express();
function rawBody(req, res, next) {
req.setEncoding('utf8');
req.rawBody = '';
req.on('data', function(chunk) {
req.rawBody += chunk;
});
req.on('end', function(){
next();
});
}
app.configure(function(){
app.set('port', process.env.PORT || 3000);
app.use(rawBody);
//app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(app.router);
});
app.post('/test', function(req, res) {
console.log(req.is('text/*'));
console.log(req.is('json'));
console.log('RB: ' + req.rawBody);
console.log('B: ' + JSON.stringify(req.body));
res.send('got it');
});
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
test.js
var request = require('request');
request({
method: 'POST',
uri: 'http://localhost:3000/test',
body: {'msg': 'secret'},
json: true
}, function (error, response, body) {
console.log('code: '+ response.statusCode);
console.log(body);
})
Hope this helps.
Express understands by content-type how to decode a body.
It must have specific decoders in middlewares, which is embedded into the library from 4.x:
app.use(express.text())
app.use(express.json())
Two important things to achieve this.
You need to add the text middleware in order to process text in the body
You need to set the content type by adding the right header "Content-type: text/plain" in the request
Here is the sample code for both.
const express = require('express');
const app = express();
const bodyParser = require('body-parser')
//This is the needed text parser middleware
app.use(bodyParser.text());
app.post('/api/health/', (req, res) => {
res.send(req.body);
});
const port = process.env.PORT || 3000;
app.listen(port, () => console.log(`Listening on ${port} ${new Date(Date.now())}`));
Save this as index.js.
Install dependencies.
npm i -S express
npm i -S body-parser
Run it.
node index.js
Now send a request to it.
curl -s -XPOST -H "Content-type: text/plain" -d 'Any text or json or whatever {"key":value}' 'localhost:3000/api/health'
You should be able to see it sending back whatever you posted.
You can use the plainTextParser (https://www.npmjs.com/package/plaintextparser) middleware..
let plainTextParser = require('plainTextParser');
app.use(plainTextParser());
or
app.post(YOUR_ROUTE, plainTextParser, function(req, res) {
let text = req.text;
//DO SOMETHING....
});
I did it:
router.route('/')
.post(function(req,res){
var chunk = '';
req.on('data', function(data){
chunk += data; // here you get your raw data.
})
req.on('end', function(){
console.log(chunk); //just show in console
})
res.send(null);
})
Make sure the version of express and bodyParser has been upgraded to the appropriate versions.
Express ˜4.x and bodyParser ˜1.18.x. That should do it. With that in place the following should work
app.use(bodyParser.text());
I have almost finished adding a new feature on my application trying to pass a json file from angular frontend to node backend via express. The original code come from How do I write a JSON object to file via Node server?. My controller part code look like:
var app = angular.module('myApp',[]);
app.controller('myCtrl',function ($scope, $http){
$scope.saveImage = function(){
var data = JSON.stringify(canvas);
debugger;
$http({
url: 'http://localhost:8080',
method: "POST",
data: data,
header: 'Content-Type: application/json'
});
}
});
I can see from developer tool that the json string "data" do have been created and has lots of things in it in my controller front end. But while I am passing it to node side, I can't get anything from req.body.
Here is my backend code:
// set up
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({extended: false}));
var saveCount = 1;
//functions
// application -------------------------------------------------------------
app.use(express.static('./public'));
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.post('/', function (req, res) {
fs.writeFile(__dirname+"/save/post"+saveCount.toString()+".json", req.body, function(err) {
if(err) {
return console.log(err);
}
res.send('The file was saved!');
});
saveCount++;
});
//listen
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
Every time when I check my psot.json files, it only have [object Object]
I have exactly no idea what is happening here.
A few things:
You don't need to call JSON.stringify() on your data object. Just pass the object to $http as is.
You need to pass bodyParser.json() not bodyParser.urlencoded().
JSON.stringify() should be called before you try to write to your file.
Your code should look like:
var app = angular.module('myApp',[]);
app.controller('myCtrl',function ($scope, $http){
$scope.saveImage = function(){
debugger; // What does this do?
$http({
url: 'http://localhost:8080',
method: "POST",
data: canvas,
header: 'Content-Type: application/json'
});
}
});
And
// set up
var express = require('express');
var app = express();
var fs = require('fs');
var bodyParser = require("body-parser");
app.use(bodyParser.json());
var saveCount = 1;
//functions
// application -------------------------------------------------------------
app.use(express.static('./public'));
app.get('/', function (req, res) {
res.send('Hello World!');
});
app.post('/', function (req, res) {
fs.writeFile(__dirname+"/save/post"+saveCount.toString()+".json", JSON.stringify(req.body), function(err) {
if(err) {
return console.log(err);
}
res.send('The file was saved!');
});
saveCount++;
});
//listen
app.listen(8080, function () {
console.log('Example app listening on port 8080!');
});
That [object Object] means that your object is actually making it to the server and being parsed properly, that's just the representation of that object. You really don't need to stringify on the front end, however. $http will handle sending the object for you, you are just creating extra work for your application. And while bodyParser may be picking up the post data (possibly because of how you've handled it on the front end), what you really want is for bodyParser to parse it as a JSON object with the .json() method, because that's what it is.
If you don't want to go through all of that, you could keep things as you have it now and change your Content-Type to application/text or similar so that it's not parsed as an object when it gets to the server.
I have tried every answer I've found on s/o, and I'm sure I must be missing something. What doesn't error on me instead gives me a 404. I tried answers from Organize routes in Node.js, strongloop's route-separation pattern, the answers from How to include route handlers in multiple files in Express?, hit similar errors as in Router.use requires middleware function? but none of those answers worked, either. The answer for Unable to Split Routes into Separate Files in Express 4.0 doesn't error, but also 404s. It seems like each answer has a different syntax and style, and maybe it's that I'm mixing and matching incorrectly?
Right now my /routes/persons.js has this pattern:
var express = require('express');
var persons = express.Router();
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
module.exports = persons;
In my server.js file, I've got:
var persons = require('./routes/persons');
app.use('/persons', persons);
This combination doesn't throw errors, but it also doesn't do anything. I've tried adding the endpoint to server.js lines:
var persons = require('./routes/persons');
app.get('/persons/:user_id', persons.addpersons);
and stripping persons.js down to just export functions:
exports.addpersons = function (req, res, next) {
var list = req.body;
// etc
}
Plus variations like wrapping the whole person.js file in module.exports = function(), sticking module.exports = router at the end, using app instead of router, etc.
What am I overlooking? Should I be adding some other middleware, rearranging how I call the endpoint, using app, or sticking with router.route? What are the most likely culprits when there's no error but the endpoint is still 404'ing?
many thanks in advance!
============= EDITED TO INCLUDE SERVER.JS =============
Since it's clear something is set wrong, somewhere, here's my server.js file:
var express = require('express');
var app = express();
var methodOverride = require('method-override');
var mongoose = require('mongoose');
var bodyParser = require('body-parser');
var router = express.Router();
var jwt = require('jsonwebtoken');
var config = require('./config');
var nodemailer = require('nodemailer');
var bcrypt = require('bcrypt-nodejs');
var crypto = require('crypto');
var async = require('async');
var transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: 'email#gmail.com',
pass: 'password'
}
});
// I don't know if both are necessary, used multiple conflicting tutorials
app.use(require('express-session')({
secret: 'secret',
resave: false,
saveUninitialized: false
}));
app.set('superSecret', config.secret);
var Schema = mongoose.Schema,
Person = require('./models/person.js'),
User = require('./models/user.js'),
Event = require('./models/event.js');
var port = process.env.PORT || 8080;
mongoose.connect(config.database);
app.use(bodyParser.json());
app.use(bodyParser.json({ type: 'application/vnd.api+json' }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(methodOverride('X-HTTP-Method-Override'));
app.use(express.static(__dirname + '/public'));
// routes go here
app.use('/api', router);
app.listen(port);
console.log('gogogo port ' + port);
I have no idea where else I might look for why including routes requires such a break in the usual pattern. My config files? My procfile? Those are the only other files sitting on the server, not counting /models and /routes.
The key here is to understand what app.use() does to your req object (in particular to req.path), how app.get() and friends are different, and how Express wraps path-to-regexp (its internal path matching module) to handle routes.
1) app.use(path, middleware) mounts the middleware. Inside the mounted middleware/router, req.path is relative to the mount path. Only the beginning of the request path needs to match, so /foo will work for requests at /foo (relative path will be /), /foo/bar (relative path is /bar), etc.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.use('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Try running the setup above, navigate to localhost/foo and see the following logs:
Main: GET /foo
In /foo: GET /
2) app.get(path, middleware), app.post(path, middleware) etc. do not mount the target middlewares, so req.path is preserved. req.path must match the whole pattern you defined your route with, so /foo will only work for /foo requests.
app.use(function (req, res, next) {
console.log('Main: %s %s', req.method, req.path);
next();
});
app.get('/foo', function (req, res) {
console.log('In /foo: %s %s', req.method, req.path);
res.send('Got there');
});
Navigate to localhost/foo and see :
Main: GET /foo
In /foo: GET /foo
3) app.route(path), as explained in the Express docs, is just a convenience to define multiple app.get(middleware), app.post(middleware) etc. sharing the same path.
Now in your case, here is a working setup:
main
var persons = require('./routes/persons');
app.use('/persons', persons);
routes/persons.js
var router = require('express').Router();
router.route('/:user_id')
.post(function (req, res) {
// handle potato data
})
.get(function (req, res) {
// get and send potato data
});
module.exports = router;
This is convenient as you only have to set the /persons entry point once in your main file, so you can easily update it later on if needed (you could also import that path value from a config file, from your router object or whatever, Node is pretty flexible in this regard). The persons router itself takes care of its business controllers, regardless of where it is exactly mounted at.
I FIGURED IT OUT!
Of course, this might be the totally wrong way to go about it (pls tell me if so) but it WORKS.
in my server.js file, I have:
var persons = require('./routes/persons');
router.get('/persons/:user_id', persons);
router.post('/persons/:user_id', persons);
and my persons.js file now looks like this:
var mongoose = require('mongoose');
var express = require('express');
var router = express.Router();
var Schema = mongoose.Schema,
Person = require('../models/person.js');
router.post('/persons/:user_id', function (req, res) {
var potatoBag = req.body;
Person.collection.insert(potatoBag, function onInsert(err, potatoBag) {
if (err) {
return res.json(err);
} else {
res.status(200).end();
}
});
});
router.get('/persons/:user_id', function(req, res) {
var id = req.params.user_id;
Person.find({'user_id':id},function(err, person) {
if (err)
return res.json(err);
res.send(person);
});
});
module.exports = router;
This seems like more overhead than most of the examples, but maybe it's because of a) using router.route and b) using imported schemas? I also had (req, res, next) in there, and it threw fits until I removed the next pieces. Probably still a bit awkward, but hey, it's working. Thanks for the help, everyone!
instead of
persons.route('/persons/:user_id')
.put(function (req, res, next) {
// etc
});
do:
persons.put('/persons/:user_id',function (req, res, next) {
// etc
});
I have my own rest API, that internally calls an NLP API, for which I have to post some data on their URL.
I am using needle to achieve this, but there's some error that I cannot catch, and my own api is returning 500 to the frontend.
Here is that part of my server.js code:
app.post('/api/get',function(req,res) {
//console.log(req);
console.log("here in post ");
if(!req.body){
return res.send(400);
}
//console.log(req.body.msg);
var searchQuery = req.body.msg;
var options = { 'api-key' : '3080a0e0-1111-11e5-a409-7159d0ac8188' };
needle.post('http://api.cortical.io:80/rest/text/keywords?retina_name=en_associative',searchQuery,options,function(err, resp){
if(err){
console.log('something went wrong :' + err);
}
console.log('Got :'+resp );
});
I reach here in post every time, but nothing after that. I am also curious that is this correct way to specify my api-key for the external API.
Thanks.
if you are using express 4.x, I am not sure whether you configured your express server but you need install body-parser and add the following lines in your express configuration:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));
app.use(bodyParser.json());
If you are using express 3.x version, you don't need to install body-parser:
var express = require('express');
var app = express();
app.use(express.json());
app.use(express.urlencoded());
Now regarding your post route I edited it a little bit:
var config = require('./config');
app.post('/api/get', function (req, res) {
var searchQuery = {
q: req.body.msg
};
var NEEDLE_API_KEY = config.needle.API_KEY;
var NEEDLE_URL = config.needle.URL;
var options = {
'api-key': NEEDLE_API_KEY
};
needle.post(NEEDLE_URL, searchQuery, options, function (err, needleResponse) {
console.log(err || needleResponse.body);
res.json(err || needleResponse.body);
});
});
So I added a new file called config.js for purposes of having a reference for all your api keys, urls of your third party services.
module.exports = {
needle: {
API_KEY: process.env.NEEDLE_API_KEY,
URL: 'http://api.cortical.io:80/rest/text/keywords?retina_name=en_associative'
}
};
So when you run your server at the console, you should pass setting a value to your global environment variable called NEEDLE_API_KEY:
NEEDLE_API_KEY=666 node app.js
So in this way you are not saving any keys on your source code, you are saving keys in global environment variables that available only on the server machine.
Edit: i fixed it by using:
app.configure(function(){
app.use(express.bodyParser());
});
Original post:
I'm trying to figure out how to handle a post with node and express and i'm completely stuck.
After some reading i noticed people saying i should use 'middleware' whatever that means and create a line app.use(express.bodyParser());. I assumed that after adding that i would have a req.body available in my post method. This isn't the case however. It console.log's into a undefined.
I think I don't know how to properly set this up, so here goes nothing:
var express = require('express')
, routes = require('./routes')
, user = require('./routes/user')
, http = require('http')
, path = require('path')
, UserProvider = require('./userprovider').UserProvider,
qs = require('querystring');
var userProvider = new UserProvider('localhost', 27017);
var app = express(),
server = require('http').createServer(app),
io = require('socket.io').listen(server);
server.listen(8080);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
app.get('/new_game', function (req, res) {
res.sendfile(__dirname + '/new_game.html');
});
app.post('/new_game', function(req, res) {
var codeToUse = Math.random()*10000;
codeToUse = Math.round(codeToUse);
console.log(req.body);
});
app.use(express.bodyParser());
app.listen(3000);
Though you've said now your code works, but i won't suggest you to use bodyParser in the options of
app.configure()
What it does is that, if you use it as you have done, any file can be send into your system for all post requests. It's better if you use
express.json()
and
express.urlencoded()
in the options of
app.configure(),
and when you expect a file use bodyParser in the respective post route like this
app.post('/upload', express.bodyParser(), function(req, res){//do something with req.files})