This question already has an answer here:
NodeJS callback with null as first argument
(1 answer)
Closed 9 months ago.
This is a code I found in the internet using Express and CORS:
const express = require('express');
const app = express();
const cors = require('cors');
var corsOptionsDelegate = async (req, callback) => {
var corsOptions = { origin: false };
try {
...
corsOptions.origin = true;
} catch (err) {
console.log(err);
}
callback(null, corsOptions)
}
app.use(cors(corsOptionsDelegate));
I don't know how this function callback(null, corsOptions) process in this code.
This is just a standard callback design pattern. Here's an article you can read but also you should learn more about what design patterns are and how Node.js works. This is a very basic question you're asking here.
You actually do not need any function callback for simple servers.
There is a much simpler way.
const whiteList = [ "https://myRealBackendUrl-1", "https://myRealBackendUrl-2" ];
// you can also pass a string here instead here instead of array
const corsOptions = {
credentials: true,
origin: process.env.NODE_ENV !== production ? "http://localhost:3000" : whiteList
// if you are in a dev environment, you probably want something like localhost
// if you are in a production environment, for example heroku then your backend
// url will be something like http://example.herokuapp.com
// in that case `const whiteList = [ "http://example.herokuapp.com" ];`
};
app.use(cors(corsOptions));
The above code should be enough for the normal use case.
As for the callback function, it is if you want to run some function of your own.
var corsOptionsDelegate = async (req, callback) => {
var corsOptions = { origin: false };
try {
// you can do some dynamic check here
// For example: check database for some conditions then allow access
if( myDatabaseSays == true ) corsOptions.origin = true;
else corsOptions.origin = false;
} catch (err) {
console.log(err);
// corsOptions.origin = false;
}
callback(null, corsOptions) // chain it
}
Anyway read the docs properly for more info
[1]: https://expressjs.com/en/resources/middleware/cors.html
Related
The below is my server.js code of my React app's backend.
I am not able to understand the working of the below line
app.use(cors(options));
The full code is as below:
const express = require("express");
const app = express();
const cors = require("cors");
let allowed = ["http://localhost:3000", "http://localhost:4000"];
function options(req, res) {
let tmp;
let origin = req.header("Origin");
if (allowed.indexOf(origin) > -1) {
tmp = {
origin: true,
optionSuccessStatus: 200,
};
} else {
tmp = {
origin: false,
};
}
res(null, tmp);
}
app.use(cors(options));
app.listen(8000, () => {
console.log("Server running at 8000");
});
app.get("/", (req, res) => {
res.send("Hi From server hello super");
});
My doubt
The "options" is a function which has arguments . But in the line app.use(cors(options)), how the arguments are passed
res(null,tmp) -> what does it do. Is it equivalent to res.send(tmp)?
This is using the Express-CORS middleware which has some documentation describing what it means to have options be a function.
Specifically, the options function is called by the middleware as the request is being processed to determine the impact on the specified fields like origin, method, optionsSuccessStatus (by the way, your code has a typo for this field—its missing an s), etc.
As the documentation shows, res is just a callback that lets the rest of the middleware and other middlewares continue normal execution.
I'm trying to get node and swagger to work with nginx dynamically
server_name ~^backend(?<PORTSERVER>[^.]+)\.domain\.com$;
location /swagger
{
proxy_pass http://127.0.0.1:$PORTSERVER/swagger/;
}
location /api
{
proxy_pass http://127.0.0.1:$PORTSERVER/api;
}
this is an example of virtual host the PORTSERVER variable is taking from gitlab-ci it takes id number of merge request + 2000
when i put the port directly in place of $PORTSEVER every thing is working swagger and api
any advice is appreciated thank you
this is index.js file
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
require("reflect-metadata");
const typeorm_1 = require("typeorm");
const express = require("express");
// var router = express.Router();
const fileUpload = require("express-fileupload");
const bodyParser = require("body-parser");
const routes_1 = require("./routes");
const cors = require("cors");
const typeorm_pagination_1 = require("typeorm-pagination");
const swaggerUi = require('swagger-ui-express');
const swaggerDocument = require('../../swagger.json');
var path = require('path');
typeorm_1.createConnection()
.then((connection) => __awaiter(void 0, void 0, void 0, function* () {
// create express app
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded());
app.use(fileUpload());
app.use(express.static(path.join(__dirname, '..', 'public')));
console.log(path.join(__dirname, '..', 'public'));
// register express routes from defined application routes
routes_1.Routes.forEach((route) => {
app[route.method]('/api' + route.route, (req, res, next) => {
const result = new route.controller()[route.action](req, res, next);
res.header('Access-Control-Allow-Origin', '*');
if (result instanceof Promise) {
result.then((result) => (result !== null && result !== undefined ? res.send(result) : undefined));
}
else if (result !== null && result !== undefined) {
res.json(result);
}
});
});
// setup express app here
// ...
app.use('/swagger', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use(express.json());
app.use(cors());
app.use(typeorm_pagination_1.pagination); // Register the pagination middleware
// start express server
// app.listen(process.env.SERVER_Port);
app.listen(process.env.PORTSERVER);
console.log('Express server has started on port ' + process.env.PORTSERVER);
}))
.catch((error) => console.log(error));
//# sourceMappingURL=index.js.map
The key is his sentence is "when i put the port directly in place of $PORTSEVER every thing is working swagger and api"
Based on the description you gave, I think that gitlabci is miss generating the port number, or miss understanding the syntax.. Both gitlabci and nginx uses $VAR syntax.. Can be a miss interpretation of the 1st line regex too..
Also, I think you need to check the content of process.env.PORTSERVERa used in the js file.. It can have different port than nginx..
For this, I would approach the issue by preventing the the job from restarting nginx to not cause down time for other vhosts.. Deploy a broken config then from the server I run nginx -t and/or diff -u a working config and a broken one..
The 1st source of truth would be nginx -t and nginx logs.. If, ever, nginx manage the starts, the HTTP code it's returning can reveal more paths to pursuit.
One thing you forgot to share is the content of your gitlabci YML.. That can help identify the issue too.
All,
I am trying to create a simple proxy which forwards all requests verbatum to another server. To do this I'm using the "http-proxy" npm. I am trying to go from local to a cloud server. At first when I setup the http-proxy I saw an error "unable to verify the first certificate". After some research online I found it's probably related to the fact that I have a self-signed certificate. Because it's self-signed it's not in the certificate store and so can't be validated. But, beacause I don't need this during development, I added "secure: false" to ignore certificate verification. I know that's unsafe from production, but I'm just trying to get around this for now. This update actually got around this error.
Now, I am getting another error "UNABLE_TO_VERIFY_LEAF_SIGNATURE".
Can any one help me figure out how to get rid of this error? I've tried adding this:
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
But that still shows the error. I see the error in the event emitted by the http-proxy (See code below for this event). If I drill down itno the 'proxyRes' I can see this error in the proxyRes -> connection -> authorizationError -> UNABLE_TO_VERIFY_LEAF_SIGNATURE
Here is my code below:
'use strict'
require('dotenv').config({silent: true})
var util = require('util');
const loggerFactory = require('./utils/logger')
const express = require('express')
const defaultRouter = require('./routes/default')
var logger = loggerFactory.consoleLogger
const proxy = require('http-proxy');
module.exports = (config) => {
const app = express()
// app.use(loggerFactory.requestLogger())
app.set('json spaces', 2)
app.set('port', config.express.port)
app.use('', defaultRouter)
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
var apiProxy = proxy.createProxyServer({});
var proxyUrl = process.env.HOMEINSPECTIONSERVER_URL;
app.use((req,res,next) => {
apiProxy.web(req, res,
{
target: proxyUrl,
secure: false,
}
);
apiProxy.on('error', function(e) {
logger.error("Error during proxy call!")
logger.error("This is the error : " + e)
next('route')
});
apiProxy.on('proxyReq', function(proxyReq, req, res, options) {
logger.info("---REQUEST---")
console.log("---REQUEST---")
// logger.info(util.inspect(proxyReq))
proxyReq.setHeader('X-Special-Proxy-Header', 'foobar');
});
apiProxy.on('proxyRes', function (proxyRes, req, res) {
// logger.info("---RESPONSE---")
// logger.info(util.inspect(proxyRes))
// logger.info("---RESPONSEEND---")
logger.info('RAW Response from the target',
JSON.stringify(proxyRes.headers, true, 2));
});
apiProxy.on('open', function (proxySocket) {
proxySocket.on('data', hybiParseAndLogMessage);
});
apiProxy.on('close', function (res, socket, head) {
console.log('Client disconnected');
});
apiProxy.on('start', function (req, res, target) {
// console.log('Started Request!');
});
})
app.use((req, res) => {
// logger.info('starting request...')
res.json(res.locals.standardResponse)
})
app.use((err, req, res, next) => {
var statusCode = 500
if (res.locals.standardResponse) {
res.locals.standardResponse.error = err
statusCode = err.statusCode || 600
logger.error(err)
res.status(statusCode).json(res.locals.standardResponse)
}
if (err.error !== undefined && err.error.httpStatus !== undefined) {
statusCode = err.error.httpStatus
} else {
statusCode = err.statusCode || 600
}
logger.error(err)
res.status(statusCode).json(res.body)
})
return app
}
I had the same problem using http-proxy-middleware the problem is solved by adding secure: false like this
const {createProxyMiddleware} = require('http-proxy-middleware');
app.use('/firmware-images/:firmwareImageId/files/:fileId/download', createProxyMiddleware({ target: `${FILES_URL}`, changeOrigin: true, secure: false }));
I know is not the same package, but it is the same problem, so I hope it helps someone.
For any one also having this problem above. I solved it by using the npm package called express-http-proxy. You can get it here:
enter link description here
So my code now looks like this:
'use strict'
require('dotenv').config({silent: true})
const loggerFactory = require('./utils/logger')
const express = require('express')
const defaultRouter = require('./routes/default')
var logger = loggerFactory.consoleLogger
module.exports = (config) => {
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
const app = express()
app.set('json spaces', 2)
app.set('port', config.express.port)
app.use('', defaultRouter)
var proxy = require('express-http-proxy');
app.use(proxy(process.env.HOMEINSPECTIONSERVER_URL))
return app
}
Note the important piece of code here:
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'
Hope that helps anyone who is stuck!
Is it possible to define a unique request Id that is included in each log statement without handing the logger to each method/function call?
Technologies in use: NodeJS, Express, Winston
Edited
Finally, I have created a library that makes all the work.
https://github.com/davicente/express-logger-unique-req-id
It is a wrapper of Winston library, so you can use it the same way.
Let me know if it helps you
We had this same problem in several projects, and I couldn't finde any complete solution for this question. We are using same technologies (Node.js, Express.js and Winston for logs)
I found a solution to this using a couple of libraries and wrapping Winston library:
- node-uuid for creating unique identificators for each request
- continuation-local-storage for sharing this IDs among different modules without sending req object in all the calls.
First I need to create and set the unique identificator with each request. I do it in the middleware:
var uuid = require('node-uuid');
var createNamespace = require('continuation-local-storage').createNamespace;
var myRequest = createNamespace('my request');
// Run the context for each request. Assign a unique identifier to each request
app.use(function(req, res, next) {
myRequest.run(function() {
myRequest.set('reqId', uuid.v1());
next();
});
});
After that I had to wrap Winston library, recovering the id from the context and adding to the message of the log:
var winston = require('winston');
var getNamespace = require('continuation-local-storage').getNamespace;
// Wrap Winston logger to print reqId in each log
var formatMessage = function(message) {
var myRequest = getNamespace('my request');
message = myRequest && myRequest.get('reqId') ? message + " reqId: " + myRequest.get('reqId') : message;
return message;
};
var logger = {
log: function(level, message) {
winstonLogger.log(level, formatMessage(message));
},
error: function(message) {
winstonLogger.error(formatMessage(message));
},
warn: function(message) {
winstonLogger.warn(formatMessage(message));
},
verbose: function(message) {
winstonLogger.verbose(formatMessage(message));
},
info: function(message) {
winstonLogger.info(formatMessage(message));
},
debug: function(message) {
winstonLogger.debug(formatMessage(message));
},
silly: function(message) {
winstonLogger.silly(formatMessage(message));
}
};
module.exports = logger;
I think it was a little bit complex, so I decided to write it down in a post. You can get more information from there: Express.js: Logging info with global unique request ID – Node.js
I hope this helps with your problem.
This answer has a problem: the counter goes back to 0 every time the node process is restarted. Turns out there is fairly simple to work around. You simply add an express middleware that tags each request called with a UUID using the uuid package.
For uuid Pre-2.0
const uuid = require('uuid');
const app = express();
app.use(function (req, next) {
req.id = uuid.v4();
next();
});
For uuid 3.0+
const uuidv4 = require('uuid/v4');
const app = express();
app.use(function (req, next) {
req.id = uuidv4();
next();
});
At the very beginning of your request handling add something like the following (or put it in its own file):
var makeID = (function() {
var index = 0;
return function() {
return index++;
}
})();
app.use(function(req, res, next) {
req.id = makeID()
next()
})
This will give every request a unique (sequential) id. Do not allow people to identify themselves with it, only use it internally!
When logging in Winston, you can enter metadata, to be displayed after the message (in the form ${name}=${value}), which looks like this
app.use(function(req, res) {
winston.log('info', 'Test Log Message', { id: req.id });
res.end("Done.")
});
Hope this is helpful.
I am building a cross system admin app, which will be used as an admin tool for multiple backend systems. The app is built on top of Mean.js.
I have setup a /proxy route using "express-http-proxy" to send all sub-routes to their respective backend system endpoints. However, I need to have each request authenticated within my admin app and then decorated with the targeted backendSystem credentials before the "express-http-proxy" can continue. Here's an example of my /proxy route...
app.use('/proxy', users.requiresLogin, expressHttpProxy(config.backendSystem.host, {
forwardPath: function (req) {
return '/1.0' + require('url').parse(req.url).path;
},
decorateRequest: function (req) {
req.headers['content-type'] = 'application/json';
req.headers['backend-system-id'] = config.backendSystem.id;
req.headers['backend-system-key'] = config.backendSystem.key;
return req;
}
}));
NOTE:
Currently the backendSystem credentials are stored based on the environment my admin app is ran in. However, in the future the backendSystem credentials will be specified by the user, and this /proxy route will differently than what is currently shown.
THE ISSUE:
Proxy routes that require data within the request body don't work.
e.g. POST /comments {"user": user_id, "text": "rabble rabble rabble"}
WHAT I'VE FOUND:
bodyParser.json() and "express-https-proxy" don't play nice. I've confirmed this by removing bodyParser.json() from express.js.
However, this isn't a full solution since almost all of my other routes need bodyParser.json, e.g. /auth/signin.
Does anyone have a clean way that I can make a route exception for my /proxy route so that bodyParser.json won't be called for it?
As far as I understand, the root of problem is so:
if you were reading a POST request by pure node, you should be using a code like this
if (req.method == 'POST') {
console.log("POST");
var body = '';
req.on('data', function (data) {
body += data;
console.log("Partial body: " + body);
});
req.on('end', function () {
console.log("Body: " + body);
});
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('post received');
}
in other words, you need to use the req.on('data') & req.on('end') events.
but the problem is,that you can use this code only once. after the 'end' is called, the request is consumed.
so then you use bodyParser , it consumes the request, and the proxy have nothing to do with it.
actually, in my opinion, the proxy wait for the 'data' event to appear , but it will newer happen, so the code halts.
The solution:
you need to 're-enable' the events. I used this code and it works for me.
var express = require('express');
var bodyParser = require('body-parser');
var http = require('http');
//call for proxy package
var devRest = require('dev-rest-proxy');
//init express (as default)
var users = require('./routes/users');
var app = express();
app.use(bodyParser.json());
//set the proxy listening port
app.set('port', 8080);
//process the POST request
app.post('/users/*', function(req, res) {
//just print the body. do some logic with it
console.log("req.body: ",req.body);
//remove listeners set by bodyParser
req.removeAllListeners('data');
req.removeAllListeners('end');
//add new listeners for the proxy to use
process.nextTick(function () {
if(req.body) {
req.emit('data', JSON.stringify(req.body));
}
req.emit('end');
});
//forward the request to another server
devRest.proxy(req,res, 'localhost', 3000);
});
//start the proxy server
http.createServer(app).listen(app.get('port'), function(){
console.log("Express server listening on port " + app.get('port'));
});
module.exports = app;
the solution found on schumacher-m post (github of nodejitsu)
I was able to resolve my issue by adding a regex that excluded my /proxy route to where bodyParser.json was being added within express.js. I found that from this answer
While this approach doesn't scale well, it solved my immediate issue.
I get it works by converting the data into query string using 3rd party query-string as follows:
proxyReqBodyDecorator: function(bodyContent, srcReq) {
return (queryString.stringify(bodyContent));
}
Have tried JSON.stringify but not working, need the data in the following format
array_field=val1&array_field=val2&array_field=val3......
To modify the request body, do this with the latest express-http-proxy v1.6.2:
const express = require('express');
const proxy = require('express-http-proxy');
const bodyParser = require('body-parser');
const conf = {
proxyHost: 'some.example.net:9200',
proxyOptions: {
proxyReqBodyDecorator: modifyRequestBody,
preserveHostHdr: true,
parseReqBody: true
},
port: 8073
};
var app = express();
app.use('/proxy', proxy(conf.proxyHost, conf.proxyOptions));
function modifyRequestBody(body, srcReq) {
if(srcReq.method.match(/^(GET|POST)$/i)) {
try {
// convert buffer to string, then to object
var str = Buffer.from(body).toString('utf-8');
var reqBody = JSON.parse(str);
if(someCondition)) {
reqBody.addStuff = 'whatever';
body = reqBody; // return modified body as object
}
} catch(error) {
console.log('- error: ' + JSON.stringify(error));
}
}
return body; // return original buffer, or modified object
}
app.listen(conf.port, function () {
log('app listening on port ' + conf.port);
});
You can fill the proxyReq.bodyContent inside the decorateRequest method with the JSON-ed data from originalReq.body to be correctly POST'ed:
app.use('/proxy', users.requiresLogin, expressHttpProxy(config.backendSystem.host, {
...
...
decorateRequest: function (proxyReq, originalReq) {
...
...
if (originalReq.body) {
proxyReq.bodyContent = JSON.stringify(originalReq.body);
}
return proxyReq;
}
...
...
}));