I am trying to post my form data to my database. When I click the submit button nothing happens. It should be calling my addDiscovery method in the discoverCtrl, but it just does nothing. I am getting no errors and have watched several tutorials in which (to me) I don't see the difference in my code and what is presented. Please tell this Angular noob what he is doing wrong!...Thanks in advance.
discoverCtrl.js:
angular.module('app')
.controller('discoverCtrl', ['$scope', '$http', function($scope, $http, $log) {
$scope.title = "Discover It!";
$scope.objects = ['animal', 'art', 'food', 'landform', 'object', 'plant', 'pokemon', 'vehicle'];
this.discovery = {};
$scope.addDiscovery = function() {
this.discovery.image = "";
this.discovery.location = ""/*gps api call*/;
this.discovery.discoveredOn = Date.now();
console.log("I'm posting something maybe?");
$http.post('/discover', {discovery: this.discovery}).success(function(data) {
});
this.discovery = {};
};
}]);
discover.html:
<h1>{{title}}</h1>
<form name="discoverForm" ng-controller="discoverCtrl" ng-submit="discoverForm.$valid && discoverCtrl.addDiscovery" novalidate>
<input ng-model="discoverCtrl.discovery.name" type="text" class="form-control" placeholder="What is the name of your discovery?" title="name" required />
<select ng-model="discoverCtrl.discovery.objectType" class="form-control" ng-options="object for object in objects" title="objectType" required>
<option value="" disabled selected>Select what type of discovery you have found.</option>
</select>
<textarea ng-model="discoverCtrl.discovery.description" class="form-control" placeholder="What does your discovery look/feel/smell like? Are there any other observations or knowledge you can share about it?" title="description" required></textarea>
<!--location found by gps TODO-->
<div>Discovery is {{discoverForm.$valid}}</div>
<input type="submit" ng-click="discoverCtrl.addDiscovery" class="btn btn-primary pull-right" value="Discover It!" />
</form>
server.js (sorry for the length. The route is at the bottom, but I didn't know if it could have been something else in here as well so I wanted to include it.):
// requires express and body-parser
var express = require('express');
var bodyParser = require('body-parser');
var expressSessions = require('express-session');
var logger = require('morgan');
var passport = require('passport');
// Google oauth2 login, key, and secrets
var GoogleStrategy = require('passport-google-oauth20').Strategy;
var configAuth = require('./auth');
// OAuth 2.0-based strategies require a `verify` function which receives the
// credential (`accessToken`) for accessing the Facebook API on the user's
// behalf, along with the user's profile. The function must invoke `cb`
// with a user object, which will be set at `req.user` in route handlers after
// authentication.
passport.use(new GoogleStrategy(configAuth.googleAuth,
function(accessToken, refreshToken, profile, cb) {
console.log('access token is', accessToken)
return cb(null, profile);
}));
// Configure Passport authenticated session persistence.
//
// In order to restore authentication state across HTTP requests, Passport needs
// to serialize users into and deserialize users out of the session. In a
// production-quality application, this would typically be as simple as
// supplying the user ID when serializing, and querying the user record by ID
// from the database when deserializing.
passport.serializeUser(function(user, cb) {
cb(null, user);
});
passport.deserializeUser(function(obj, cb) {
cb(null, obj);
});
//requiring and setting up mongo database/collections
var mongojs = require('mongojs');
var databaseUrl = "discoverIt";
var collections = ["users", "discoveries"];
// creates a databse in mongo called scrape with two collections: articles and comments
var db = mongojs('discoverIt', ['users', 'discoveries']);
// lets us know if there is an error with the database if it doesn't turn on
db.on('error', function(err) {
console.log('Database Error: ', err);
});
// creating an instance of express
var app = express();
// assigning the port or using the PORT environment variable
var PORT = process.env.PORT || 3000;
// makes static content in assets accessible
app.use(express.static(__dirname + '/public'));
// // BodyParser interprets data sent to the server
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.text());
app.use(bodyParser.json({type: 'application/vnd.api+json'}));
// Use application-level middleware for common functionality, including
// logging, parsing, and session handling.
app.use(require('morgan')('combined'));
app.use(require('cookie-parser')());
app.use(require('body-parser').urlencoded({ extended: true }));
app.use(require('express-session')({ secret: 'keyboard cat', resave: true, saveUninitialized: true }));
// Initialize Passport and restore authentication state, if any, from the
// session.
app.use(passport.initialize());
app.use(passport.session());
// Define routes.
app.get('/', function(req, res) {
res.sendFile(__dirname + '/public/views/index.html');
});
app.get('/login',
function(req, res){
res.sendFile(__dirname + '/public/views/login.html');
});
app.get('/login/google',
passport.authenticate('google', {scope: 'profile'}));
app.get('/login/google/return',
passport.authenticate('google', { failureRedirect: '/login' }),
function(req, res) {
res.redirect('/profile');
});
app.get('/profile',
require('connect-ensure-login').ensureLoggedIn(),
function(req, res){
db.users.insert({
"email": "",
"avatar": req.user.photos[0].value,
"userName": req.user.displayName,
"discoveries": 0,
"edits": 0,
"confirms": 0,
"points": 0
});
res.send({ user: req.user });
});
app.post('/discover', function(req, res){
db.discoveries.insert({
"user": req.user.displayName,
"userId": req.user.id,
"image": req.body.discovery.discovery.image,
"name": req.body.discovery.discovery.name,
"objectType": req.body.discovery.discovery.objectType,
"description": req.body.discovery.discovery.description,
"location": req.body.discovery.discovery.location,
"discoveredOn": req.body.discovery.discovery.discoveredOn
});
//*********************************
//increase user discovery count
//**********************************
});
//******************************************************
//starts the server letting user know the PORT
app.listen(PORT, function(){
console.log("listening on port %d", PORT);
}); // end of app.listen
As #dustmouse said, I needed the ()...oops!
Related
The problem I currently have is that when I send my form data in React using axios or even without it I am unable to retrieve my data on the backend side using express. I am viewing the request received on the back end console but am unable to access it via code. React code is via Node on port 3000, Express on 3001. Application was created using create-react-app, here is a picture:Problem
Project Structure:
cssd -> Where I run npm start launching back end
cssd/routes/ -> Where users.js is located with the routes to obtain code
cssd/client/ -> Where i run npm start launching front end
cssd/client/src/setupProxy.js -> where my proxy route is
I've already tried creating a setupProxy.js file as well as numerous get / post methods trying to receive the data and any of the info available on stack has been searched as well but if I've missed something please let me know. Even with a console.log at the base, i still can't receive it.
Within setupProxy:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/routes/*',
{ target: "http://localhost:3001/" }
));
}
Within client's App.js / React:
render() {
return (
<form id="contact-form" onSubmit={this.handleSubmit.bind(this)} method="POST">
<div className="form-group">
<label htmlFor="name">Name</label>
<input type="text" className="form-control" id="name"/>
</div>
<div className="form-group">
<label htmlFor="exampleInputEmail1">Email address</label>
<input type="text" className="form-control" id="email" aria-describedby="emailHelp"/>
</div>
<div className="form-group">
<label htmlFor="message">Message</label>
<textarea className="form-control" rows="5" id="message"></textarea>
</div>
<button type="submit" className="btn btn-primary">Submit</button>
</form>
)
}
handleSubmit(e)
{
e.preventDefault();
const name = document.getElementById('name').value;
const email = document.getElementById('email').value;
const message = document.getElementById('message').value;
var inputData = {name, email, message};
fetch('/routes/users',
{
method: 'POST',
body: inputData
})
alert("sent data");
this.resetForm();
}
Within routes/users.js / Express code:
var express = require('express');
var router = express.Router();
const bodyParser = require("body-parser");
const app = express();
app.use(
bodyParser.urlencoded({ // basically any extra characters
extended: true
}),
bodyParser.json()
);
// Re-directs, ex entered in nothing, go to get
router.get("/routes/users", function(req, res) {
console.log("HERE 1 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
router.post("/routes/users", function(req, res) {
console.log("HERE 2 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
app.get("/routes/users", function(req, res) {
console.log("HERE 3 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
app.post("/routes/users", function(req, res) {
console.log("HERE 4 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
router.get("routes/users", function(req, res) {
console.log("HERE 5 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
router.post("routes/users", function(req, res) {
console.log("HERE 6 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
app.get("routes/users", function(req, res) {
console.log("HERE 7 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data: ", name, email, message);
});
app.post("routes/users", function(req, res) {
console.log("HERE 8 !");
var name = req.body.name;
var email = req.body.email;
var message = req.body.message;
console.log("The data", name, email, message);
});
const port = 3000;
//console.log(`Back end is listening on port ${port}`);
app.listen(port, () => console.log(`Back end is listening on port ${port}`));
module.exports = router;
The expected results should be that I receive a console.log from one of the functions and can then work with my code from there but I'm just unable to get any of them to be called. Only thing shown in the console is on the back-end side receiving the request
Remove all proxying from the server as this is only if you are serving the client and server from the same host and port. So make sure your app.listen is on a different port than your client like so.
const port = process.env.PORT || 5000
app.listen(port, () => console.log(`listnening on ${port}`));
Is this app set up with Create React App? If so you can add a proxy to the package.json of the client (React)
"proxy": "http://localhost:5000"
Create react app uses webpack dev server to serve your app to the browser. You can use a proxy from your client to achieve want you want.
In your package.json of your react app, add "proxy": "http://localhost:3001"
Here is more info about this from the create react app docs.
Your backend code does not need any changes to make this work, in fact I would even remove all proxy setup code in your backend. Only thing needed is to add this one setting in your react app package.json.
I am trying use socket.io + angular to create a chat room web application.
I have a specific text box in which the user enter the name of the room he wants to connect. then, I am trying to connect this user into that specific room and send all of his messages to this room.
however, when I am trying to send a string into the chat, the request is being handle on the server side (the console log output is printed from the server) but not on the controller socket.on('chat_message', function(data) ...) method does not print it console log.
what am I doing wrong?
the code is below. there is more code that I don't think is required (angular related stuff).
Thanks in advance
view
<form ng-submit="submit()">
<input autocomplete="off" ng-model="insertedText" type="text" />
<button type="button" ng-click="submit()">
Send
</button>
</form>
controller
mymodule.controller("cntrlChat", ['$scope', 'myService',
function($scope, myService){
var socket = io();
$scope.messages = [];
$scope.room= myService.get();
socket.emit('room', $scope.room);
$scope.submit=function(){
socket.emit('chat_message',{ room: $scope.room, msg: $scope.user+": "+$scope.insertedText });
$scope.insertedText='';
return false;
}
socket.on('chat_message', function(data){
console.log('room-->'+data.room+' msg---->'+data.msg );
$scope.$apply(function() {
$scope.messages.push(data.msg);
});
});
}]);
server
var express = require('express');
var app = express();
var http = require('http').Server(app);
var io = require('socket.io')(http);
app.use(express.static(__dirname + '/'));
app.get('/', function(req, res){
res.sendFile(__dirname + '/index.html');
});
io.on('connection', function(socket){
io.emit('chat_message', "welcome");
socket.on('room', function(room) {
socket.join(room);
});
socket.on('chat_message', function(data){
console.log("room---->"+data.room, "msg---->"+data.msg);
io.sockets.in(data.room).emit('chat message',data.msg);
});
socket.on('disconnect', function(){
io.emit('chat message', "Bye");
});
});
http.listen(3000, function(){
console.log('listening on *:3000');
});
You are emitting chat message from the server and waiting for chat_message on the client. Look carefully, one has an underscore the other a space.
It might just be a typo error.
Avoid spaces and special characters in message labels.
the error is here
io.sockets.in(data.room).emit('chat message',data.msg);
it should be
io.sockets.in(data.room).emit('chat_message',data.msg);
im currently trying to save data in express sessions.
Im using the following packages:
"connect-mongo": "^0.8.2"
"express": "~4.0.0"
"express-session": "^1.11.3"
"mongoose": "~3.6.13"
I'm implementing the sessions this way:
// Loading packages
var express = require('express');
var app = express();
var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
// Connect to MongoDB
var mongoose = require('mongoose');
var options = { server: { socketOptions: { keepAlive: 1, connectTimeoutMS: 30000 } },
replset: { socketOptions: { keepAlive: 1, connectTimeoutMS : 30000 } } };
mongoose.connect(process.env.DATABASE, options);
var router = express.Router();
// Session storage
router.use(session({
secret: 'keyboard cat',
resave: false,
saveUninitialized: true,
store: new MongoStore({mongooseConnection: mongoose.connection}),
cookie: {
maxAge: 60*60*24*7*1000
}
}));
// Routes
router.route('/user/me')
.get(function(req, res) {
console.log(req.session);
});
router.route('/auth/login')
.post(function(req, res) {
req.session.userId = req.params.id;
});
app.use('/api', router);
var port = process.env.PORT || 8080;
app.listen(port);
console.log('Magic happens on port ' + port);
Now i have the following problem:
When i try posting to /api/auth/login using a software called "Postman REST Client" (Chrome extension) it works, and the user id will be logged when getting /api/user/me.
But if i try posting to /api/auth/login in an angular.js app, it does not save the session values. I always get a clean session object without the userId.
Also there are no cookies saved.
Could you please help me?
Greetings: Max
I've been struggling with this for a couple hours now and need some help. I've created a simple app that presents the user with a "Login Using Google" button in an angular view that redirects the user to the Google Oauth page. Here's the controller code that calls the login() function when the button is pressed:
angular.module('dashApp').controller('SigninCtrl', function ($scope) {
$scope.login=function() {
var client_id="191641883719-5eu80vgnbci49dg3fk47grs85e0iaf9d.apps.googleusercontent.com";
var scope="email";
var redirect_uri="http://local.host:9000/api/auth/google";
var response_type="code";
var url="https://accounts.google.com/o/oauth2/auth?scope="+scope+"&client_id="+client_id+"&redirect_uri="+redirect_uri+
"&response_type="+response_type;
window.location.replace(url);
};
});
The redirect URI set in my google project redirects to a server page to this server page:
'use strict';
var _ = require('lodash');
var request = require('request');
var qs = require('querystring');
var fs = require('fs');
// Get list of auths
exports.google_get = function (req,res){
var code = req.query.code,
error = req.query.error;
if(code){
//make https post request to google for auth token
var token_request = qs.stringify({
grant_type: "authorization_code",
code: code,
client_id: "191641883719-5eu80vgnbci49dg3fk47grs85e0iaf9d.apps.googleusercontent.com",
client_secret: process.env.GOOGLE_SECRET,
redirect_uri: "http://local.host:9000/api/auth/google"
});
var request_length = token_request.length;
var headers = {
'Content-length': request_length,
'Content-type':'application/x-www-form-urlencoded'
};
var options = {
url:'https://www.googleapis.com/oauth2/v3/token',
method: 'POST',
headers: headers,
body:token_request
};
request.post(options,function(error, response, body){
if(error){
console.error(error);
}else{
//WHAT GOES HERE?
}
});
}
if(error){
res.status(403);
}
}
I'm able to exchange the code returned by google for an auth token object successfully and log it to the terminal. I've been told that I should set a cookie using:
res.setHeader('Content-Type', 'text/plain');
res.setCookie('SID','yes',{
domain:'local.host',
expires:0,
path:'/dashboard',
httpOnly:false
});
res.status(200);
res.end();
Followed by a controller on the page I'm directing the user to that validates the session.
What am I doing wrong?
Since you have already done the hard work so there is no point talking about passport.js which is actually written to simplify these kind of social login authentication.
So let's come directly to session implementaion logic.
You need to set the following header in your app.js/server.js :
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST');
res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With,content-type, Authorization');
next();
});
Let's say you are returning this token after successful login :
{
name : "some name",
role : "some role",
info : "some info"
}
You can have a client side function in your angular service or controller :
function(user,callback){
var loginResource = new LoginResource(); //Angular Resource
loginResource.email = user.email;
loginResource.password = user.password;
loginResource.$save(function(result){
if(typeof result !== 'undefined'){
if(result.type){
$localStorage.token = result.token;
$cookieStore.put('user',result.data);
$rootScope.currentUser = result.data;
}
}
callback(result);
});
}
LoginResource calls your REST endpoint which returns auth token.
You can store your auth token in localStorage and cookieStore.
localStorage makes sure that we are having the token saved even when user has closed the browser session.
If he clears the localStorage and cookieStorage both then log him out as you don't have any valid token to authorize user.
This is the same logic which i am using here. If you need more help then let me know.
I would like to add a CSRF protection in my app which uses a MEAN stack.
I tried the answer already given by someone : CSRF Protection in ExpressJS
But it was for the older express version, so I made some changes :
app.use(cookieParser(config.cookieSecret, { httpOnly: true }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({ secret: config.sessionSecret, resave: false, saveUninitialized: true }));
app.use(passport.initialize());
app.use(passport.session());
app.use(flash());
app.use(csrf({value: function(req) {
var token = (req.body && req.body._csrf)
|| (req.query && req.query._csrf)
|| (req.headers['x-csrf-token'])
|| (req.headers['x-xsrf-token']);
return token;
}
}));
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
I can see the token named XSRF-TOKEN is well generated on my app (using Chrome inspection).
But when I post a form (Angular frontend), I have an error about the token :
{"message":"invalid csrf token","error":{"expose":true,"code":"EBADCSRFTOKEN","statusCode":403,"status":403}}
Did I miss something ? I'm wondering if req.csrfToken() generates the good token given by Angular...
EDIT :
I just see the XSRF-TOKEN is used by AngularJS in $http requests only. So I think I have to add a hidden input in my form to post with csrf value, to be checked by Express, but how ?
Finally I was able to send the good token value. Here's the complete answer.
AngularJS uses CSRF protection (called XSRF by Angular) during requests made with $http service.
When performing XHR requests, the $http service reads a token from a
cookie (by default, XSRF-TOKEN) and sets it as an HTTP header
(X-XSRF-TOKEN). Since only JavaScript that runs on your domain could
read the cookie, your server can be assured that the XHR came from
JavaScript running on your domain. The header will not be set for
cross-domain requests.
There are many posts which explained how to send XSRF-TOKEN with ExpressJS 3.xx, but some things change with 4.xx version. Connect middlewares are not included anymore. Express uses its own middelware : cookie-parser, body-parser, express-session, and csurf.
1 - Send XSRF-TOKEN cookie
The first step is to send the cookie, from the backend to the frontend (Express to Angular) :
var express = require('express');
var app = express();
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var config = require('./lib/config'); //config.js file with hard-coded options.
var csrf = require('csurf');
app.use(cookieParser(config.cookieSecret, { httpOnly: true }));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
name: 'sessionID',
secret: config.sessionSecret,
cookie: {
path: '/',
httpOnly: true,
secure: false,
maxAge: 3600000
},
rolling: true,
resave: false,
saveUninitialized: true
}));
app.use(csrf());
app.use(function(req, res, next) {
res.cookie('XSRF-TOKEN', req.csrfToken());
next();
});
Now Angular is able to set its HTTP header (X-XSRF-TOKEN) during $http requests. Example :
<body ng-app="testApp">
<div ng-controller="LoginCtrl">
<form role="form" ng-submit="login()" >
<input type="email" ng-model="user.email" />
<input type="password" ng-model="user.password" />
<input type="submit" value="Log In" />
</form>
<p style="color:red">{{loginMsg}}</p>
</div>
</body>
<script>
var app = angular.module('testApp', ['ngResource']);
app.controller('LoginCtrl', function ($scope, $http) {
$scope.user = {};
$scope.loginMsg = '';
$scope.login = function () {
$http.post('/login', $scope.user).success(function () {
$window.location.href='/profile';
}).error(function () {
$scope.loginMsg = 'Wrong credentials, try again.';
});
};
});
</script>
2 - Add a _csrf input in non-Angular forms
That was my issue, I didn't know how to add this input. Finally I created a new Angular service named $csrf :
app.factory('$csrf', function () {
var cookies = document.cookie.split('; ');
for (var i=0; i<cookies.length; i++) {
var cookie = cookies[i].split('=');
if(cookie[0].indexOf('XSRF-TOKEN') > -1) {
return cookie[1];
}
}
return 'none';
});
I made an example on Plunker : http://plnkr.co/edit/G8oD0dDJQmjWaa6D0x3u
Hope my answer will help.
if anyone gets "csurf configuration error", try this:
app.use( csrf( { cookie: true } ));
I have created repository that you can directly use.
node
https://github.com/nil4you/node-csrf-and-http-only-cookie-with-spa
angular
https://github.com/nil4you/angular-csrf-and-httponly-cookie-demo
ask any question here any comment, as I can't see much coding issue, it's all about right setup issue.