data is not being sent by POST, reads OPTIONS in console - reactjs

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

Related

React profile page, how to avoid 'GET http://localhost:3001/users/profile 401 (Unauthorized)' when trying to get JSON data from back end

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');
}
});

API upload by Multer + Nodejs (code: 'LIMIT_UNEXPECTED_FILE', field: 'files[]', storageErrors: [])

I'm using Multer on server side for file upload
Midleware :
const util = require("util");
const path = require("path");
const multer = require("multer");
var storage = multer.diskStorage({
destination: (req, file, callback) => {
callback(null, path.join(`${__dirname}../../upload`));
},
filename: (req, file, callback) => {
const match = ["image/png", "image/jpeg"];
if (match.indexOf(file.mimetype) === -1) {
var message = `<strong>${file.originalname}</strong> is invalid. Only accept png/jpeg.`;
return callback(message, null);
}
var filename = `${Date.now()}-bezkoder-${file.originalname}`;
callback(null, filename);
}
});
var uploadFiles = multer({ storage: storage }).array("files", 10);
var uploadFilesMiddleware = util.promisify(uploadFiles);
module.exports = uploadFilesMiddleware;
Controller
const multipleUpload = async (req, res) => {
try {
await upload(req, res);
console.log('Check logs file ', req.Content - Type);
if (req.files.length <= 0) {
return res.send(`You must select at least 1 file.`);
}
return res.send(`Files has been uploaded.`);
} catch (error) {
console.log(error);
if (error.code === "LIMIT_UNEXPECTED_FILE") {
return res.send("Too many files to upload.");
}
return res.send(`Error when trying upload many files: ${error}`);
}
};
module.exports = {
multipleUpload: multipleUpload
};
Client capture Payload
My problem in upload event the server side show error messenger
Please help me to solve it.
Thanks
I have tried upload by axios is not problem but
I'm using the tool react Devextreme --> FileUploader . The supporter of devextreme did n't support server side .

Multer File Upload Not Working in Firefox

Multer adds the uploaded files to req.file or req.files. My code works in Chrome as expected, but both req.file and req.files are empty in both Firefox (68.01) and Edge (44.17763.1.0).
Why does the request object doesn't contain the files in Firefox and Edge?
My multer config:
const path = require('path');
const uuidv4 = require('uuid/v4');
const multer = require('multer');
const projectRoot = require('./projectRoot');
// Allowed audio mime types
const audioMimeTypeToExt = {
'audio/wav': '.wav',
'audio/mp3': '.mp3',
};
// Allowed image mime types
const imageMimeTypeToExt = {
'image/jpeg': '.jpg',
'image/png': '.png'
};
// Storage options
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, path.join(projectRoot.path, '/public/tracks/'))
},
filename: function (req, file, cb) {
if (file) {
// New file name is uuid + extension (i.e. '10ba038e-48da-487b-96e8-8d3b99b6d18a.mp3')
cb(null, uuidv4() + audioMimeTypeToExt[file.mimetype])
} else {
console.log('File missing.');
}
}
});
// Filter options
const filter = function (req, file, cb) {
if (!(file.mimetype.toLowerCase() in audioMimeTypeToExt)) {
// Reject upload
cb(null, false);
}
// Accept upload
cb(null, true);
};
const uploadMiddleware = (req, res, next) => {
multer(
{
storage: storage,
fileFilter: filter,
limits: {fileSize: 50000000} //50mb
}
).array('tracks', 10)(req, res, function (err) {
if (err && err.code === "LIMIT_FILE_SIZE") {
return res.status(413).send(err.message);
}
next();
});
};
module.exports = {
audioMimeTypeToExt,
imageMimeTypeToExt,
uploadMiddleware: uploadMiddleware,
};
My route
const express = require('express');
const router = express.Router();
const sessionMiddleware = require('../middlewares/sessionMiddleware');
const {uploadMiddleware} = require('../config/multerConfig');
const uploadController = require('../controllers/uploadController');
router.post('/',
sessionMiddleware.requiredLogin,
uploadMiddleware,
uploadController.upload_post
);
module.exports = router;
My controller
const {processUploads} = require('../utilities/uploadsProcessor');
exports.upload_post = async function (req, res, next) {
if (req.files && req.files.length) { // req.files empty in Firefox and Edge
await processUploads(req);
console.log('Upload successful.');
res.status(200).end();
}
else {
res.status(404).end();
}
};
Thank you
Figured out what was going wrong. Firefox and Edge have different values in the file.mimetype field. For MP3 files, Chrome adds 'audio/mp3', while Firefox and Edge add 'audio/mpeg'. So my filter was filtering our files that don't contain 'audio/mp3' or 'audio/wav' mimetypes.

Fields in Mongo are not displayed

I have the following problem, after making a mongo scheme like this:
let Books = new Schema({
video_ru: String,
name: {
ru: String,
uz: String,
en: String
},
only field video_ru comes in db.
When there were no categories, everything worked as it should.
here is my Route
const express = require('express');
const booksRoutes = express.Router();
// Require books model in our routes module
let Books = require('./books.model');
// Defined store route
booksRoutes.route('/add').post(function (req, res) {
let books = new Books(req.body);
books.save()
.then(books => {
res.status(200).json({'books': 'books is added successfully'});
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});
// Defined get data(index or listing) route
booksRoutes.route('/').get(function (req, res) {
Books.find(function(err, bookses){
if(err){
console.log(err);
}
else {
res.json(bookses);
}
});
});
// Defined edit route
booksRoutes.route('/edit/:id').get(function (req, res) {
let id = req.params.id;
Books.findById(id, function (err, books){
res.json(books);
});
});
// Defined update route
booksRoutes.route('/update/:id').post(function (req, res) {
Books.findById(req.params.id, function(err, books) {
if (!books)
res.status(404).send("data is not found");
else {
books.name.ru = req.body.name_ru;
books.subname.ru = req.body.subname_ru;
books.description.ru = req.body.description_ru;
books.logo.ru = req.body.logo_ru;
books.video_ru = req.body.video_ru;
books.name.uz = req.body.name_uz;
books.subname.uz = req.body.subname_uz;
books.description.uz = req.body.description_uz;
books.logo.uz = req.body.logo_uz;
books.name.en = req.body.name_en;
books.subname.en = req.body.subname_en;
books.description.en = req.body.description_en;
books.logo.en = req.body.logo_en;
books.save().then(books => {
res.json('Update complete');
})
.catch(err => {
res.status(400).send("unable to update the database");
});
}
});
});
// Defined delete | remove | destroy route
booksRoutes.route('/delete/:id').get(function (req, res) {
Books.findByIdAndRemove({_id: req.params.id}, function(err, books){
if(err) res.json(err);
else res.json('Successfully removed');
});
});
module.exports = booksRoutes;
in the end I get in the database this json
[{"_id":"5d2d94ca6206e73ff02e920d","video_ru":"","__v":0}]
but need with name fields
Well! Your schema is right. Your schema is nested so you have to save value like below or use bodyparser to wrap forms input to body.
booksRoutes.route('/add').post(function (req, res) {
let books = new Books(req.body);
books.name = {
ru: req.body.ru,
uz: req.body.ru,
en: req.body.ru
};
books.save()
.then(books => {
res.status(200).json({'books': 'books is added successfully'});
})
.catch(err => {
res.status(400).send("unable to save to database");
});
});

How to Get Param Id from URL in express/mongo/mongoose on server side, axios/react/redux on client side

I am having some trouble with get the param from the url. I use Express(4.16.3) on the server side, and using Axios to make the request. But I couldn't seem to get the param from the url in Express.
Here is my code:
on my Route.js in Express
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(req.params);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
so instead of getting the actual id, it logs params: {surveyId: ':surveyId'}. I have been researching, but seems this is the correct way to do it. I also use axios to make the request:
in actions/index.js (I use react):
export const fetchOneSurvey = () => async dispatch => {
const res = await axios.get('/api/surveys/:surveyId');
dispatch({ type: FETCH_ONE_SURVEY, payload: res.data });};
Not sure if this is relevant:
On the view page, instead of having http://localhost:3000/api/surveys/:surveyId, I have http://localhost:3000/surveys/:surveyId route set in React. When I go to http://localhost:3000/surveys/:surveyId, it does console log (req.params) like I write in express, but I only get a string ':surveyId' is the params, not the actual id on the url.
Please anyone can help me? I have tried many different ways, but nothing seem working. I thank you all very much in advance.
===== Extra section ======
Here is my index.js:
const express = require('express');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const bodyParser = require('body-parser');
const keys = require('./config/keys');
require('./models/User');
require('./models/Survey');
require('./services/passport');
mongoose.connect(keys.mongoURI);
const app = express();
app.use(bodyParser.json());
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
);
app.use(passport.initialize());
app.use(passport.session());
require('./routes/authRoutes')(app);
require('./routes/billingRoutes')(app);
require('./routes/surveyRoutes')(app);
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'));
const path = require('path');
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'));
});
}
My survey model route js:
const _ = require('lodash');
const Path = require('path-parser');
const { URL } = require('url');
const mongoose = require('mongoose');
const requireLogin = require('../middlewares/requireLogin');
const requireCredits = require('../middlewares/requireCredits');
const Mailer = require('../services/Mailer');
const surveyTemplate = require('../services/emailTemplates/surveyTemplate');
const Survey = mongoose.model('surveys');
module.exports = app => {
app.get('/api/surveys', requireLogin, async (req, res) => {
const surveys = await Survey.find({ _user: req.user.id }).select({
recipients: false
});
res.send(surveys);
});
app.get('/api/surveys/:surveyId/:choice', (req, res) => {
res.send('thanks for voting');
});
app.get('/api/surveys/:surveyId', (req, res, next) => {
var id = req.params.surveyId;
console.log(id);
// it gets params {surveyId: ':surverId'}
res.send('Hello World');
});
app.post('/api/surveys/webhooks', (req, res) => {
// console.log(req.body);
// res.send({});
const p = new Path('/api/surveys/:surveyId/:choice');
const test = _.chain(req.body)
.map(({ email, url }) => {
const match = p.test(new URL(url).pathname);
if (match) {
return {
email,
surveyId: match.surveyId,
choice: match.choice
};
}
})
.compact()
.uniqBy('email', 'surveyId')
.each(({ surveyId, email, choice }) => {
Survey.updateOne(
{
// have to add _ to keys as mongoDB rule, mongoose doensn't need.
_id: surveyId,
recipients: {
$elemMatch: { email: email, responded: false }
}
},
{
$inc: { [choice]: 1 },
$set: { 'recipients.$.responded': true },
lastResponded: new Date()
}
).exec();
})
.value();
console.log(test);
res.send({});
});
app.post('/api/surveys', requireLogin, requireCredits, async (req, res) => {
const { title, subject, body, recipients } = req.body;
const survey = new Survey({
// map(email => ({ email }) === map(email =>{ return {email: email}})
title,
body,
subject,
recipients: recipients
.split(',')
.map(email => ({ email: email.trim() })),
_user: req.user.id,
dateSent: Date.now()
});
// send an email
const mailer = new Mailer(survey, surveyTemplate(survey));
try {
await mailer.send();
await survey.save();
req.user.credits -= 1;
const user = await req.user.save();
res.send(user);
} catch (err) {
res.status(422).send(err);
}
});
};
Posting below details for debugging the issue
Note: if you are using Windows OS, use command prompt for node project development. i have seen people using git bash for doing node project developments and it causes unnecessary issues
Below are the steps for debugging
1.Create a new directoryforexample test and initialize it using npm init
2.Install express npm install --save express
3.Create a new file for example index.js and use below code
test/index.js
var express= require("express");
var app = express();
app.get("/api/surveys/:surveyId",(req,res,next)=>{
console.log(req.params.surveyId);
res.send('Hello World');
});
var server= app.listen(3000,()=>{
console.log("port started at ",server.address().port);
})
4.Start the program node index.js
5.Trigger http request from browser http://localhost:3000/api/surveys/llads . The value llads can be accessed using the path param surveyId in the route
6.if you can see the below output in node console then the program is working as it should. And this has to work as described here.
if above steps yields expected output then i don't see any problem in your route code.
Let me know your feedback.

Resources