Middleware issue with body-parser:
The data gets stored as an object which later calls /api/new to post in SQL database.
No Issues with the query itself but data has null values in the database.
My thoughts:
app.use(express.urlencoded({
extended: true
}));
is set to true, as some values are INT,
I tried JSON.Stringify the data before it gets sent to the POST request. Did not work
Also played around with the header
// const config = {
// headers: {
// accept: "application/json",
// "accept-language": "en_US",
// "content-type": "application/x-www-form-urlencoded"
// }
// };
axios.post("/api/new",{newEntry}, config ...
I think the issue may be due to the fact that its router.post in my api-routes instead of app.post
```
var newEntry = {
First_Name: FirstName.toUpperCase(),
Last_Name: LastName.toUpperCase(),
Employ_ID: parseInt(EmployID),
Problem: checkedValue,
PhoneNumber: phoneNumber,
Provider: domain
};
var stringNewEntry = JSON.stringify(newEntry);
console.log("Right before Post");
console.log(newEntry);
axios
.post(
"/api/new",
{newEntry},
{
timeout: 10000
}
)
.catch(error => {
if (error.code === "ECONNABORTED") return "timeout";
})
.then(res => {
console.log(newEntry);
if (res === "timeout") {
console.log("Please Try Again");
} else {
console.log("SUCCESS!");
}
});
}
````
Server File
const express = require("express");
const app = express();
var bodyParser = require('body-parser')
const PORT = process.env.PORT || 3001;
var routes = require("./routes/api-routes");
// Define middleware here
app.use(express.json());
app.use(bodyParser.json());
app.use(express.urlencoded({
extended: true
}));
// Serve up static assets (usually on heroku)
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
}
// Add routes, both API and view
app.use(routes);
// Start the API server
app.listen(PORT, function() {
console.log(`🌎 ==> API Server now listening on PORT ${PORT}!`);
});
api-routes
router.route("/api/new").post(function(req, res) {
console.log("Data:");
console.log(req.body.First_Name);
var dbQuery =
"INSERT INTO KioskInfo (First_Name,Last_name,Employ_ID,Problem,PhoneNumber,Provider) VALUES (?,?,?,?,?,?)";
var dbQueryPermanent =
"INSERT INTO KioskData (First_Name,Last_name,Employ_ID,Problem) VALUES (?,?,?,?)";
var IgnoreQuery =
"INSERT IGNORE INTO KioskInfo (First_Name,Last_name,Employ_ID,Problem,PhoneNumber,Provider) VALUES (?,?,?,?,?,?)";
connection.query(
dbQuery,
[
req.body.First_Name,
req.body.Last_Name,
req.body.Employ_ID,
req.body.Problem,
req.body.PhoneNumber,
req.body.Provider
],
function(err, result) {
if (err) {
connection.query(
IgnoreQuery,
[
req.body.First_Name,
req.body.Last_Name,
req.body.Employ_ID,
req.body.Problem,
req.body.PhoneNumber,
req.body.Provider
],
function(IgnErr, IgnResult) {
if (IgnErr) {
throw IgnErr;
} else {
console.log("Duplicate Entry Ignored");
}
}
);
}
//Learn to specify DUP ERR;
console.log("Successfully Saved Into KioskInfo");
}
);
connection.query(
dbQueryPermanent,
[
req.body.First_Name,
req.body.Last_Name,
req.body.Employ_ID,
req.body.Problem
],
function(err, result) {
if (err) throw err;
console.log("Successfully Saved Into KioskData");
res.end();
}
);
});
Related
For this application, I am using React & Express. I have React running on PORT 3000, and Express running on PORT 3001. On the Express side, I have authentication working that uses JWT.
First, here is my auth.js service file:
const jwt = require('jsonwebtoken');
const models = require('../models');
const bcrypt = require('bcryptjs');
var authService = {
signUser: function (user) {
const token = jwt.sign({
Username: user.Username,
UserId: user.UserId
},
'secretkey',
{
expiresIn: '1h'
}
);
return token;
},
verifyUser: function (token) {
try {
let decoded = jwt.verify(token, 'secretkey');
return models.users.findByPk(decoded.UserId);
} catch (err) {
console.log(err);
return null;
}
},
hashPassword: function (plainTextPassword) {
let salt = bcrypt.genSaltSync(10);
let hash = bcrypt.hashSync(plainTextPassword, salt);
return hash;
},
comparePasswords: function (plainTextPassword, hashedPassword) {
return bcrypt.compareSync(plainTextPassword, hashedPassword);
}
}
module.exports = authService;
When a user makes a POST request to the signup route, it works:
router.post('/signup', function (req, res, next) {
models.users.findOrCreate({
where: {
Username: req.body.username
},
defaults: {
FirstName: req.body.firstName,
LastName: req.body.lastName,
Email: req.body.email,
Password: authService.hashPassword(req.body.password)
}
})
.spread(function (result, created) {
if (created) {
res.redirect("http://localhost:3000/login");
} else {
res.send('This user already exist')
}
});
});
Signup works in both Postman and React.
When a user makes a POST request to the login route, it works:
router.post('/login', function (req, res, next) {
models.users.findOne({
where: {
Username: req.body.username
}
}).then(user => {
if (!user) {
console.log('User not found')
return res.status(401).json({
message: "Login Failed"
});
} else {
let passwordMatch = authService.comparePasswords(req.body.password, user.Password);
if (passwordMatch) {
let token = authService.signUser(user);
res.cookie('jwt', token);
res.redirect('http://localhost:3001/users/profile');
} else {
console.log('Wrong Password');
}
}
});
});
Login works in both Postman and React.
When a user makes a GET request to the profile route, it semi-works:
router.get('/profile', function (req, res, next) {
let token = req.cookies.jwt;
if (token) {
authService.verifyUser(token).then(user => {
if (user) {
res.setHeader('Content-Type', 'application/json');
res.send(JSON.stringify(user));
} else {
res.status(401);
res.send('Invalid authentication token');
}
});
} else {
res.status(401);
res.send('Invalid authentication token');
}
});
This works only in Postman, I can see the data that I want using Postman. In React, it will not get the profile route that I request. This is where the error comes in: Console Error
On the React side, this is profile GET component:
import React from 'react';
import axios from 'axios';
class UserProfile extends React.Component {
constructor(props) {
super(props);
this.state = {
profileData: []
}
};
fetchProfileData = () => {
var encodedURI = window.encodeURI(this.props.uri);
return axios.get(encodedURI).then(response => {
this.setState(() => {
return {
profileData: response.data
};
});
});
};
componentDidMount() {
this.fetchProfileData();
}
render() {
console.log(this.state.profileData);
if (this.state.profileData.length === 0) {
return <div>Failed to fetch data from server</div>
}
const profile = this.state.profileData.map(user => (
<div key={user.UserId}>Hello world</div>
));
return <div>{profile}</div>
}
}
export default UserProfile;
Then when I go to render this component, I just:
<UserProfile uri="http://localhost:3001/users/profile" />
Which then will render 'Failed to fetch data from server', then the console will log the '401 (Unauthorized)' error. I just can't get it to render in React.
And if anyone wants my Express app.js file for some extra information:
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
var models = require('./models');
var cors = require('cors');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'hbs');
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(cors());
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function (req, res, next) {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
models.sequelize.sync().then(function () {
console.log("DB Synced Up");
});
module.exports = app;
Thank you in advanced. I have been struggling to figure this out.
I have tried toying with my UserProfile component. And I've tried toying with my /profile route in Express. The only 2 errors I've gotten is the 401 (Unauthorized) and something about the Headers. I know that my JWT key gets passed onto reacts side, because when I do 'localhost:3000/profile' (react side), I can see that I have the cookie stored. I'm not sure on how to approach authorization on React side. At this point, I am very clueless on what to do. This is the first time I've tried setting up authentication with React. I have always used Express and the .hbs files to render my profile pages. But I've been told that you shouldn't render a profile page in the back-end. So, here I am trying to do it with React.
I have rendered things from the back-end to the front-end, but that's without the use of JWT. I strongly believe that it has something to do with the JWT cookie. I just don't know how to authenticate it in React. Thanks again in advanced.
I fixed it by adding this into my React project:
I added this into my fetchProfileData()
{ withCredentials: true }
fetchProfileData = () => {
var encodedURI = window.encodeURI(this.props.uri);
return axios.get(encodedURI, { withCredentials: true }).then(response => {
this.setState(() => {
return {
profileData: response.data
};
});
});
};
Then in Express, I toyed with my Profile route. Put the data into an array, and sent it on its way:
router.get('/profile', function (req, res, next) {
var userData = [];
let token = req.cookies.jwt;
if (token) {
authService.verifyUser(token).then(user => {
userData.push(user);
res.send(userData);
});
} else {
res.status(401);
res.send('Invalid authentication token');
}
});
I'm using nodejs and run a very simple socketserver based on the examples of Google. I've basically set everything according to how they explain, my environment is flex etc. But yet everytime my application client (angular based) produces the following error in the developer console
WebSocket connection to 'wss://' failed: and falls back to https polling
It works perfectly fine when I run my application locally.
My socket app.js
'use strict';
const express = require('express');
const app = express();
const userSocketIdMap = new Map();
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.set('view engine', 'pug');
console.log("clienturl is:", process.env.clienturl);
const server = require('http').Server(app);
const io = require("socket.io")(server, {
cors: {
origin: process.env.clienturl, //this is filled with correct origin url
credentials: true,
},
allowEIO3: true
});
app.get('/', (req, res) => {
io.emit('message', { message: `GET call` });
res.status(200).send()
});
app.post('/', (req, res) => {
let message = ''
if (req.body.message.hasOwnProperty('data')) {
message = Buffer.from(req.body.message.data, 'base64').toString(
'utf-8'
);
}
let sender = '';
let uid = '';
if (req.body.message.hasOwnProperty('attributes')) {
if (req.body.message.attributes.hasOwnProperty('sender')) {
sender = req.body.message.attributes.sender;
}
if (req.body.message.attributes.hasOwnProperty('uid')) {
uid = req.body.message.attributes.uid;
}
}
if (uid) {
let recipientSocketIds = userSocketIdMap.get(uid);
if (recipientSocketIds) {
for (let socketId of recipientSocketIds) {
io.to(socketId).emit('message', { message: message, sender: sender, uid: uid });
}
}
} else {
io.emit('message', { message: message, sender: sender, uid: uid });
}
console.log({ message: message, sender: sender, uid: uid });
res.status(200).send()
});
io.on('connection', socket => {
let uid = socket.handshake.query.uid;
if (!userSocketIdMap.has(uid)) {
//when user is joining first time
userSocketIdMap.set(uid, new Set([socket.id]));
} else {
//user had already joined from one client and now joining using another
userSocketIdMap.delete(uid);
userSocketIdMap.set(uid, new Set([socket.id]));
}
console.log('client connected', process.env.clienturl,userSocketIdMap)
});
io.on('disconnect', socket => {
console.log('Got disconnect!');
});
if (module === require.main) {
const PORT = process.env.PORT || 8080;
server.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
}
module.exports = server;
And my app.yaml file
runtime: nodejs
env: flex
manual_scaling:
instances: 1
network:
session_affinity: true
React Beginner Here. I'm trying to connect an existing react project and socket.io but now I can't access the routes with app.use(). I can see the console logs of the connections and disconnections but when I login with '/login', It doesn't run. Front end runs at 3000, Server at 9000
Edit: It does run because I receive a token which is sent at the end of it. However I get a status 404 Cannot POST /login.
Socket.js
const express = require('express')
const router = express.Router()
const http = require('http')
const cors = require('cors')
const server = http.createServer(router)
router.use(cors())
const {Server} = require("socket.io")
const io = new Server(server, {
cors: {
origin: 'http://localhost:3000',
methods: ['GET', 'POST', 'PUT', 'DELETE'],
}
})
io.on('connection', (socket) => {
console.log('a user connected')
socket.on('disconnect', () => {
console.log('user disconnected', socket.id)
})
socket.on('chat message', (msg) => {
console.log('message: ' + msg)
io.emit('chat message', msg)
})
})
module.exports = {server}
Server.js
const express = require('express')
const app = express()
const {server} = require('./socket/socket')
const cors = require('cors')
app.use(cors({
origin: ['http://localhost:3000', 'http://localhost:9000', 'http://localhost:3001'],
credentials: true,
}))
app.use(express.json())
app.use(express.urlencoded({extended: true}))
const login = require('./routes/login')
const signup = require('./routes/verify/signup')
const home = require('./routes/home')
const checkRefreshToken = require('./routes/_checkRT')
const logout = require('./routes/_logout')[![enter image description here][1]][1]
const verify = require('./routes/verify/_verify')
app.use('/login', login)
app.use('/signup', signup)
app.use('/', home)
app.use('/check_refresh_token', checkRefreshToken)
app.use('/logout', logout)
app.use('/verification', verify)
const PORT = process.env.PORT || 9000
server.listen(PORT, () => {
console.log(`Listening on http://localhost:${PORT}`)
})
front end messages.jsx
import React from 'react'
import Navbar from '../../components/navbar/navbar'
import io from 'socket.io-client'
const socket = io.connect('http://localhost:9000/')
const Messages = () => {
return (
<div>
<Navbar />
</div>
)
}
export default Messages
Login.js
require('dotenv').config()
const express = require('express')
const router = express.Router()
const jwt = require('jsonwebtoken')
const cors = require('cors')
const bcrypt = require('bcrypt')
const mysql = require('mysql')
const connection = mysql.createConnection({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_DATABASE
})
// middleware
router.use(cors({
origin: ['http://localhost:3000', 'http://localhost:9000', 'http://localhost:3001'],
credentials: true,
}))
router.use(express.json())
router.use(express.urlencoded({extended: true}))
/**
* Flow of the code:
* 1. Check if email exists in the database
* 2. If it does, check if the password matches
* 3. If it does, generate an access token
* 4. Then generate a refresh token
* 5. Then send the access token and refresh token to the client
*/
router.post('/', (req, res) =>
{
// Even these doesn't run
// console.log("HERE");
// res.send("HELLO")
const { email, password } = req.body
function getInput() {
return new Promise((resolve, reject) => {
let sql = `
SELECT * FROM account_table
LEFT JOIN section_table
ON account_table.account_section_id = section_table.section_id
WHERE account_email = ?
;`
connection.query(sql, [email], (err, results) => {
if (err) return reject({status: 500, message: 'Internal server error'})
else if (results.length === 0) return reject({status: 401, message: 'Invalid email or password'})
return resolve(results)
})
})
}
function checkPassword(input) {
return new Promise((resolve, reject) => {
bcrypt.compare(password, input[0].account_password, (err, result) => {
if (err) return reject({status: 500, message: 'Internal server error'})
else if (!result) return reject({status: 401, message: 'Invalid email or password'})
return resolve(input)
})
})
}
function generateAccess(result) {
return new Promise((resolve, reject) => {
const userAccessToken = {
user_id: result[0].account_id,
user_section_id: result[0].account_section_id
}
jwt.sign(userAccessToken, process.env.ACCESS_TOKEN_SECRET, {expiresIn: '15m'}, (err, token) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(token)
})
})
}
function generateRefresh(result) {
return new Promise((resolve, reject) => {
const userAccessToken = {
user_id: result[0].account_id,
user_section_id: result[0].account_section_id
}
jwt.sign({userAccessToken}, process.env.REFRESH_TOKEN_SECRET, (err, token) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(token)
})
})
}
function insertSend(access, refresh, result) {
return new Promise((resolve, reject) => {
const userInfo = {
user_f_name: result[0].account_first_name,
user_l_name: result[0].account_last_name,
section_grade: result[0].section_grade,
section_strand: result[0].section_strand,
section_name: result[0].section_name,
}
let expiryDate = new Date(Date.now())
expiryDate.setDate(expiryDate.getDate() + 7) // 7 days
let sql = `INSERT INTO refresh_token_table (token_content, token_owner_id, token_timestamp) VALUES (?, ?, ?)`
connection.query(sql, [refresh, result[0].account_id, expiryDate], (err, results) => {
if (err) return reject({status: 500, message: 'Internal server error'})
return resolve(
res.cookie("hello_world_cookie69", refresh, {
origin: "http://localhost:9000",
expires: expiryDate,
httpOnly: true,
secure: true,
sameSite: "strict",
}).json({userInfo, access})
)
})
})
}
try {
let vEmail = await getInput()
let vData = await checkPassword(vEmail)
let accessToken = await generateAccess(vData)
let refreshToken = await generateRefresh(vData)
let send = await insertSend(accessToken, refreshToken, vData)
} catch (err) {
return res.status(err.status).json({ message: err.message })
}
})
module.exports = router
I am stuck on this problem for 2 days. I am sending POSTrequest from frontend to the backend (and other GET requests too but the problem is only with POST). However, when my data goes to the backend it does not post anything to the rest api even though response is 200 OK. That's why when in response it should have given the posted data, it can't find it and gives null. This is my POST code in backend index.js:
const { response, request } = require('express');
require('dotenv').config()
const express = require('express');
const morgan = require('morgan');
const Contact = require('./models/contact.cjs');
const cors = require('cors')
const app = express();
app.use(express.json())
app.use(express.static('build'))
app.use(cors())
morgan.token('body', req => {
return JSON.stringify(req.body)
})
app.use(morgan(':method :url :status :res[content-length] - :response-time ms :body'));
const generateId = () => {
const randNum = Math.floor(Math.random() * 5000)
return randNum;
}
app.post('/api/persons', (req, res) => {
const body = req.body
console.log(body)
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data"
})
} else if (Contact.find({name: body.name})) {
Contact.findOneAndUpdate({name: body.name}, {$set: {number: body.number}}, {new:true})
.then(updatedContacts =>
res.json(updatedContacts)
)
.catch(err => console.log(err))
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date()
})
contact.save()
.then(savedContact => {
console.log(savedContact)
res.json(savedContact)
})
.catch(err => {
console.log(err)
})
}
})
const PORT = process.env.PORT
app.listen(PORT, () => {
console.log(`Server is working on ${PORT}`)
})
and this is how my frontend sends data to backend: contacts.js:
const create = (newObject) => {
const readyToPost = {
method: 'post',
url: `${baseUrl}`,
data: newObject,
headers: {'Content-Type': 'application/json'},
json: true
}
const request = axios(readyToPost)
return request.then(response => {
console.log(response.data)
return response.data
})
.catch(err => {
console.log(err)
})
}
And this is my react app's frontend.
Any ideas about why my data becomes null?
Any help would be appreciated!
Due to the synchronous nature of your code, the condition Contact.find({name: body.name}) was always returning the Query object which is true due to which the else if block was getting executed even when there was no such document. After entering the else if block, since there was no match, so findOneAndUpdate() was returning null.
Use findOne() instead of find(). find() returns a cursor which is empty but true whereas findOne() returns the first document matched (if matched) or else it will return null (if not matched).
// index.js (Backend)
app.post("/api/persons", async (req, res) => {
const body = req.body;
if (!body.name || !body.number) {
return res.status(400).json({
error: "missing data",
});
}
// Using findOne() instead of find(). Returns null if record not found.
const existing = await Contact.findOne({ name: body.name });
if (existing) {
Contact.findOneAndUpdate(
{ name: body.name },
{ $set: { number: body.number } },
{ new: true }
)
.then((updatedContacts) => {
console.log(updatedContacts);
res.status(200).json(updatedContacts);
})
.catch((err) => console.log(err));
} else {
const contact = Contact({
id: generateId(),
name: body.name,
number: body.number,
date: new Date(),
});
contact
.save()
.then((savedContact) => {
console.log(savedContact);
res.status(201).json(savedContact);
})
.catch((err) => {
console.log(err);
});
}
});
I have a react redux app where I am posting data to my node (express) server. In my action creator the data is being sent to the server but it isn't responding to the file. Here's my action creator.
// action creator
export function addItem(product) {
return dispatch => {
dispatch(request(product));
axios.post(api + '/api/addtoinventory', { product })
.then(res => {
dispatch(success(product));
})
.catch(err => {
dispatch(failure(err.toString()));
});
}
function request(product) { return { type: ADDING_ITEM, product } }
function success(product) { return { type: ITEM_ADDED, product } }
function failure(error) { return { type: ADD_TOAST, payload: error} }
}
Then in my express file I have code like this..
// server.js
var express = require('express');
var router = express.Router();
var multer = require('multer');
var uuidv4 = require('uuid/v4');
var path = require('path');
var database = require('./database');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, '../../../adminpanel/src/0000001');
},
filename: (req, file, cb) => {
const newFilename = `${uuidv4()}${path.extname(file.originalname)}`;
cb(null, newFilename);
}
});
const upload = multer({ storage });
router.post('/', function(req, res) {
var title = req.body.product.title;
var price = req.body.product.price;
var description = req.body.product.description;
database.query("INSERT INTO `Items` (`ID`, `Title`, `Price`, `Description`, `CreateDate`) VALUES (NULL, ?, ?, ?, CURRENT_TIMESTAMP)", [title, price, description], function(err, result) {
if(err) {
console.log(err);
} else {
var id = result.insertId;
console.log(id);
}
});
});
module.exports = router;
Then when i check for the console log I just get this in return
OPTIONS /api/addtoinventory 200 10.300 ms - 4
Shouldn't that say POST instead of OPTIONS ?
You need to create a middleware which will allow CORS for your registered req origins
var allowCrossDomain = function(req, res, next) {
res.header('Access-Control-Allow-Origin', 'your domain here');
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type');
next();
}
Then in your startup file include this middleware
app.use(allowCrossDomain);
If you want to read about it more
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests