I am trying to build a web application using MVC pattern, but I have a problem with POST-request to "http://localhost:5000/api/device": postman POST request attempt
The problem only occurs on POST request, GET request is OK: get request
Code:
index.js
const express = require('express')
const sequelize = require('./db')
require('dotenv').config();
const path = require('path')
const models = require('./models/models')
const cors = require("cors")
const app = express();
const router = require('./routes/index')
const fileUpload = require('express-fileupload')
const errorHandler = require('./milddleware/ErrorHandlingMiddleware')
app.use(cors())
app.use(express.json())
app.use(express.static(path.resolve(__dirname, 'static')))
app.use(fileUpload({}))
app.use('/api', router)
app.use(errorHandler)
const PORT = process.env.PORT || 5000;
const db_start = async ()=>
{
try
{
await sequelize.authenticate();
await sequelize.sync();
app.listen(PORT, ()=>{console.log(`Server started on port ${PORT}`)})
}
catch (e)
{
console.log(e);
}
}
db_start()
routes/index.js
const Router = require('express')
const router = new Router();
const deviceRouter = require('./deviceRouter')
const typeRouter = require('./typeRouter')
const brandRouter = require('./brandRouter')
const userRouter = require('./userRouter')
router.use('/user', userRouter)
router.use('/device', deviceRouter)
router.use('/brand', brandRouter)
router.use('/type', typeRouter)
module.exports = router
routes/deviceRouter.js
const Router = require('express')
const DeviceController = require('../controllers/deviceController')
const router = new Router();
router.post('/', DeviceController.create)
router.get('/', DeviceController.getAll)
router.get('/:id', DeviceController.getOne)
module.exports = router
controllers\deviceController.js
const uuid = require('uuid')
const path = require('path')
const {Device} = require('../models/models')
const ApiError = require("../errors/ApiError")
class DeviceController
{
async create(req, res, next)
{
console.log(req);
try{
const {name, price, brandId, typeId, info} = req.body;
const {img} = req.files;
let fileName = uuid.v4() + ".jpg";
img.mv(path.resolve(__dirname, '..', 'static', fileName));
const device = await Device.create({name, price, brandId, typeId, img: fileName});
return res.json(device);
}
catch(e)
{
next(ApiError.badRequest(e.message));
}
}
async getAll(req, res)
{
const {brandId, typeId} = req.query
let devices;
if(!brandId && !typeId)
{
devices = await Device.findAll()
}
if(brandId && !typeId)
{
devices = await Device.findAll({where: {brandId}})
}
if(!brandId && typeId)
{
devices = await Device.findAll({where: {typeId}})
}
if(brandId && typeId)
{
devices = await Device.findAll({where: {brandId,typeId}})
}
return res.json(devices)
}
async getOne(req,res)
{
}
}
module.exports = new DeviceController()
I logged the request and saw that the request body came up empty and req.files is undefined.
I compared these router and controller with others and found no differences in structure.
What am I doing wrong?
You cannot retrieve files of form-data directly from req.files.
Use formidable (it's an npm package) to parse form-data and work with it easily. Here are the steps:
Run npm install formidable.
Import formidable and fs package in your controller script with:
const formidable = require('formidable')
const fs = require('fs');
Change your create function with this:
async create(req, res, next) {
let form = new formidable.IncomingForm()
form.keepExtensions = true
form.parse(req, async (err, fields, files) => {
// console.log('err', err)
// console.log('fields', fields)
if (err) {
next(ApiError.badRequest(err));
return;
}
const { name, price, brandId, typeId, info } = fields; // <-- fields contain all your text data. it's like req.body
const { img } = files; // <-- it should work now!
let fileName = uuid.v4() + ".jpg";
fs.writeFileSync(path.resolve(__dirname, '..', 'static', fileName), img);
const device = await Device.create({ name, price, brandId, typeId, img: fileName });
return res.json(device);
}
}
Related
I am trying to access req.body in express middleware in a router, however it is consoling empty req.body. However in actual data does exist. I am already using body-parser at app level but it is not working for me. Below is my code, I am trying to access req.body in authorization middleware in categories.js file
index.js
const express = require('express');
const bodyParser = require('body-parser');
//Importing Routers
const customersRouter = require('./routes/customers');
const categoriesRouter = require('./routes/categories');
const itemsRouter = require('./routes/items');
const usersRouter = require('./routes/users');
const tablesRouter = require('./routes/tables');
const ridersRouter = require('./routes/riders');
const taxtypesRouter = require('./routes/taxtypes');
const branchesRouter = require('./routes/branches');
const subscribeRouter = require('./routes/subscription');
const loginRouter = require('./routes/login');
//Importing Database Connection
//const db = require('./dbConnection');
const app = express();
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server is listening at port ${PORT}`));
app.use('/subscribe', subscribeRouter);
app.use('/login', loginRouter);
app.use('/client/:clientID/user/:userID/customers', customersRouter);
app.use('/:clientID/categories', categoriesRouter);
app.use('/client/:clientID/user/:userID/items', itemsRouter);
app.use('/client/:clientID/user/:userID/users', usersRouter);
app.use('/client/:clientID/user/:userID/tables', tablesRouter);
app.use('/client/:clientID/user/:userID/riders', ridersRouter);
app.use('/client/:clientID/user/:userID/taxtypes', taxtypesRouter);
app.use('/client/:clientID/user/:userID/branches', branchesRouter);
router file categories.js
const express = require('express');
const util = require('util');
const categoriesRouter = express.Router();
const verifyToken = require("../functions/userVarification");
const multer = require('multer');
const fs = require('fs');
var path = require('path');
//Importing Database Connection
const db = require('../dbConnection');
const query = util.promisify(db.query).bind(db);
// File Uploading through Multer
var storage = multer.diskStorage({
destination: (req, file, cb) => {
const path = `./uploads/${req.body.clientID}/categories`;
fs.mkdirSync(path, { recursive: true });
return cb(null, path);
},
filename: function (req, file, cb) {
cb(null, req.body.clientID + "_" + "Category_" + file.originalname.replace(".", "_") +"_" + Date.now() + path.extname(file.originalname)) //Appending extension
}
})
var upload = multer({ storage: storage });
function authorization(req, res, next) {
//Only Admins are allowed to add category
const roleID = parseInt(req.authData.roleID);
if (roleID !== 1) res.status(403).json({ msg: "Sorry your are not authorized to add categories" });
else {
next();
}
}
categoriesRouter.post('/', verifyToken, (req, res, next) => authorization(req, res, next), upload.single('Image'), (req, res) => {
//destructuring request body
const clientID = parseInt(req.body.clientID);
const userID = parseInt(req.body.userID);
const name = req.body.Name;
const branches = req.body.Branches;
const color = req.body.Color;
let imageSource = `uploads/${req.body.clientID}/categories/${req.file.filename}`;
const imageInPOS = req.body.ImageInPOS;
const visibilityInPOS = req.body.VisibilityInPOS;
try {
(async () => {
//Create Local Category ID
const SQL1 = `SELECT COUNT(ClientID) AS 'Categories' FROM categories WHERE ClientID = ${clientID};`;
let counter = await query(SQL1);
counter = counter[0].Categories + 1;
const localCategoryID = 'CT' + ('00' + counter).slice(-3);
//SQL for Adding Category in the database
const SQL2 = `INSERT INTO categories (CategoryID, LocalCategoryID, ClientID, CategoryName, ShowInBranches, CategoryColor, ImageSrc, DisplayInPOS, DisplayImage)
VALUES (NULL, '${localCategoryID}', ${clientID}, '${name}', '${branches}', '${color}', '${imageSource}', ${visibilityInPOS}, ${imageInPOS})`;
const addedCategory = await query(SQL2);
if(addedCategory.affectedRows > 0) {
res.status(200).json({ msg : "Category have been added"});
}
else {
res.status(500).json({ msg: "Something went wrong" });
}
})()
}
catch (err) {
res.status(500).json({ msg: "Something went wrong" });
return;
}
});
module.exports = categoriesRouter;
the body-parser module don't require to explicitly install.
Its provided under the methods express.json() and express.urlencoded().
so add
app.use(express.urlencoded({extended: true}));
app.use(express.json())
and remove
require('body-parser');
app.use(bodyParser.urlencoded({extended:false}));
app.use(bodyParser.json());
i have a problem if you can help,
i've deployed my app on heroku and it's working properly on localhost when i upload product picture when launching on localhost everything is working and the picture is added to my public folder and the url is passed to my Product image url field, but when doing the same on heroku deployed app it shows: "ENOENT: no such file or directory, open 'public/obNHUnF85G2kHtApYvix5D-1200-80-1617359184714.png"
thx in advance
Here is my app.js
const express = require('express')
const morgan = require('morgan')
const mongoose = require('mongoose')
const jwt = require('jsonwebtoken');
var cors = require('cors')
const errorHandler = require('./middleware/error')
const app = express()
var corsOptions = {
origin: "*",
optionsSuccessStatus: 200 // some legacy browsers (IE11, various SmartTVs) choke on 204
}
app.use(cors(corsOptions))
app.use(express.static('public'))
app.use(express.json())
app.use(morgan('tiny'))
require('dotenv/config')
const productRouter = require('./Routes/ProductRouter')
const categoryRouter = require('./Routes/CategoryRouter');
const authRoute = require('./Routes/auth');
const userRoute = require('./Routes/user');
const OrderRouter = require('./Routes/OrderRouter');
const imagesRouter = require('./Routes/addImagesRoute');
const { static } = require('express');
const apiUrl = process.env.API_URL
const port = process.env.PORT;
mongoose
.connect(process.env.CONNECTION_STRING, {
useUnifiedTopology: true,
useNewUrlParser: true
})
.then(() => {
console.log('connected successfully')
})
.catch(err => {
console.log(err)
})
app.use(`${apiUrl}/products`, productRouter)
app.use(`${apiUrl}/categories`, categoryRouter)
app.use(`${apiUrl}/auth`, authRoute)
app.use(`${apiUrl}/users`, userRoute)
app.use(`${apiUrl}/orders`, OrderRouter)
app.use(`${apiUrl}/images`, imagesRouter)
app.use(errorHandler);
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
My Uploading Router
const express = require('express')
const { uploadImage, uploadGalleryImages } = require('../controllers/uploadController')
const { authorize, protect } = require('../middleware/auth')
const uploadRouter = express.Router()
const multer = require('multer')
const path = require('path')
const FILE_TYPE_MAP = {
'image/png': 'png',
'image/jpeg': 'jpeg',
'image/jpg': 'jpg'
}
const storage = multer.diskStorage({
destination: function(req, file, cb) {
const isValid = FILE_TYPE_MAP[file.mimetype]
let uploadError = new Error('invalid image type')
if (isValid) {
uploadError = null
}
cb(uploadError, 'public/')
},
filename: function(req, file, cb) {
const fileName = file.originalname.split(' ').join('-')
const extension = FILE_TYPE_MAP[file.mimetype]
cb(null, `${fileName.split('.', 1)}-${Date.now()}${path.extname(file.originalname)}`)
}
})
const uploadOptions = multer({ storage: storage })
uploadRouter.use(protect)
uploadRouter.use(authorize('publisher', 'admin'))
uploadRouter.post(
'/:productId/image',
uploadOptions.single('image'),
uploadImage
);
uploadRouter.put('/:productId/images', uploadOptions.array('images', 8), uploadGalleryImages);
module.exports = uploadRouter;
Adding url and hash function
const file = req.file
if (!file) return res.status(400).send('No image in the request')
const fileName = file.filename
const basePath = `${req.protocol}://${req.get('host')}/`
try {
const product = await Product.findByIdAndUpdate(
req.params.productId, {
imageUrl: `${basePath}${file.filename.split('.', 1)}${path.extname(
file.originalname
)}`
}, { new: true }
);
await product.save();
const url = product.imageUrl;
const hash = await handler(url.toString());
const hashString = JSON.parse(hash).blurHash;
await Product.findByIdAndUpdate(req.params.productId, {
imageHash: hashString
}, { new: true })
product.save()
res.send(product)
} catch (error) {
return next(new ErrorResponse(`an error occured : ${error}`, 400))
}
})```
when i upload on localhost [Local host request and result][1]
when i upload on heroku server [Heroku][2]
[1]: https://i.stack.imgur.com/Fp8OK.png
[2]: https://i.stack.imgur.com/oaFuC.png
I have a problem with code actually server not receives JSON or receives JSON as text
.......................................................................
.......................................................................
.......................................................................
app.js:
const config = require("config");
const express = require('express')
const bodyParser = require('body-parser');
const app = express()
app.use(bodyParser.json()) //express.json was tried
app.use(bodyParser.text()) // if do not using this json null
app.use('/api/auth',require("./routes/auth.route"))
auth.route.js
const {Router} = require("express");
const config = require("config");
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const {body,validationResult} = require("express-validator");
const User = require("../models/User");
const router = Router();
//api / auth/register
router.post(
'/register',
[
body("email",'Wrong Email').notEmpty().isEmail(),
// check('password','Min Char count is 6, Password incorrect').isLength({min:6})
],
async (req,res)=>{
console.log("body",req.body)
console.log("email",req.body['email'])
req.body = JSON.parse(req.body);
console.log("body",req.body)
console.log("email",req.body['email'])
const validationErrors = validationResult(req);
console.log(validationErrors.array())
if(!validationErrors.isEmpty())
return res.status(401).json({arguments: validationErrors.array(),type:"wrongInputData"});
console.log(2)
})
and client:
import {useState, useCallback} from 'react';
const urlStart = "http://localhost:5000";
export const useHttp = () =>{
const [loadings,setLoading] = useState(false);
const [setError] = useState(null);
const request =useCallback(async (url, method = 'GET',body = null) =>{
setLoading(true);
try {
if(body) {
body = JSON.stringify(body);
}
url = urlStart+url
const response = await fetch(url,{method: method, body: body,
headers: {'Content-Type': 'application/json'},
mode:'no-cors',cache: 'no-cache',});
const data = await response.json();
if(!response.ok){
throw new Error("Smth wrong : "+data?.message);
}
setLoading(false);
return data;
}catch (e) {
setLoading(false);
setError(e.message);
throw e;
}
},[]);
const clearError = () => setError(null);
return { loadings, request,clearError}
}
edit: added a bit more code.
const express = require('express');
var bodyParser = require('body-parser');
const app = express();
var urlencodedParser = bodyParser.urlencoded({extended: false})
const {google} = require('googleapis');
const {PubSub} = require('#google-cloud/pubsub');
const iot = require('#google-cloud/iot');
const API_VERSION = 'v1';
const DISCOVERY_API = 'https://cloudiot.googleapis.com/$discovery/rest';
app.get('/', urlencodedParser, (req, res) => {
const projectId = req.query.proyecto;
const cloudRegion = req.query.region;
const registryId = req.query.registro;
const numSerie = req.query.numSerie;
const command = req.query.command;
const client = new iot.v1.DeviceManagerClient();
if (client === undefined) {
console.log('Did not instantiate client.');
} else {
console.log('Did instantiate client.');
sendCom();
}
async function sendCom() {
const formattedName = await client.devicePath(projectId, cloudRegion, registryId, numSerie)
const binaryData = Buffer.from(command);
const request = {
name: formattedName,
binaryData: binaryData,
};
return client.sendCommandToDevice(request).then(responses => res.status(200).send(JSON.stringify({
data: OK
}))).catch(err => res.status(404).send('Could not send command. Is the device connected?'));
}
});
const PORT = process.env.PORT || 8080;
app.listen(PORT, () => {
console.log(`App listening on port ${PORT}`);
console.log('Press Ctrl+C to quit.');
});
module.exports = app;
I have this function, that I call after the client initiate: sendCom();
async function sendCom() {
const formattedName = await client.devicePath(projectId, cloudRegion, registryId, deviceId)
const binaryData = Buffer.from(command);
const request = { name: formattedName, binaryData: binaryData, };
client.sendCommandToDevice(request)
.then(responses => {
res.status(200).send(JSON.stringify({ data: OK })).end();
})
.catch(err => {
res.status(404).send('Could not send command. Is the device connected?').end();
});
}
My problem is that sendCommandToDevice gets executed perfectly, however I get the catch error.
As I understand it, it's because in the .then ends the connection.
I've looked at this and thats's what I tried, however I'm not sure I understand what's going on.
You can not use send with end.
end() is used when you want to end the request and want to respond with no data.
send() is used to end the request and respond with some data.
You can found more about it here.
I have making an API using express and node.
Here is my app.js
const express = require('express');
const bodyParser = require('body-parser');
const dotenv = require('dotenv');
// setup dotenv to read environment variables
dotenv.config()
// Load Environment Varibles
const env = require('./utils/env');
// INIT MONGODB CONNECTION
require('./mongoose');
// create a new express application
const app = express();
// setup bodyparser middleware to read request body in requests
// we're only reading JSON inputs
app.use(bodyParser.json());
// Listen to API routes
const apiRoutes = require('./routes')
app.use('/api', apiRoutes);
// Start listening to requests
app.listen(env.PORT, () => {
console.log(`Server started on PORT ${env.PORT}`);
});
And here is the API routes that are being imported
const express = require('express');
const apiController = require('./apiController');
const apiValidator = require('./apiValidator');
const router = express.Router();
router.post('/login', apiValidator.loginUserValidator, apiController.loginUserController);
router.get('/rand', (req, res) => {
res.send('Some randon text');
});
module.exports = router;
Here is the middleware
const {
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserValidator = (req, res, next) => {
const user = req.body;
if (!user.username) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid username"));
}
if (!user.password) {
return res.status(400).json(failureResponse(errorcodes.ERROR_INVALID_BODY_PARAMETER, "Invalid password"));
}
if (user.authTokens) {
delete user.authTokens;
}
next();
};
module.exports = {
loginUserValidator
};
Here is the controller
const User = require('./../models/user');
const {
successResponse,
failureResponse
} = require('./../utils/response');
const errorcodes = require('./../utils/errorcodes');
const loginUserController = async (req, res) => {
try {
const user = req.body;
// find if the user already exists
const existingUser = await User.findOne({
username: user.username
});
if (existingUser) {
// user exists. generate token and login user
console.log('Existing user login');
const token = existingUser.generateAuthToken();
return res.status(200).json(successResponse(token));
} else {
console.log('New user login');
const savedUser = await new User(user).save();
const token = savedUser.generateAuthToken();
return res.status(200).json(successResponse(token));
}
} catch (e) {
console.log(e);
return res.status(400).json(failureResponse(errorcodes.ERROR_SERVER_ERROR, "Unable to login user"));
}
};
module.exports = {
loginUserController
};
Here the issue is when I try to hit the login route from Postman, I am getting an error which says Could not get any response.
But when I hit the rand route, the output is correct.
So the issue isn't the arrangement of the code.
Why am I not able to use the login route here?