I am currently authoring a component for a users sites which adds a number of resources to their server. For my part, I am wanting to use express, but the user could be using express themselves or some other web framework.
Because I want this to just work out of the box, I was attempting to setup my express pipeline via proxying http.createServer. Injecting the pipeline this way seems to work reasonably well in cases where I handle the request, but it fails in cases where I let it fall through and execute the users callback (specifically, it says that the response headers have been sent already).
Here is the sample I am working on. Any ideas?
var http = require('http');
var express = require('express');
var setupProxy = function setupProxy() {
var app = buildApp();
var oldCreateServer = http.createServer;
http.createServer = function(callback) {
return oldCreateServer(function(req, res) {n
app.apply(this, arguments);
if (!res.finished) {
callback.apply(this, arguments);
}
});
};
};
var buildApp = function buildApp() {
var app = express();
app.use('/test', function(req, res) {
res.send('Hello World');
});
return app;
};
I suspect your express handling creates a default 404 response when it doesn't match any of your routes. So, that would be the cause of the headers already sent issue when you are not handling it, but trying to pass it on.
So, I think you need your own 404 handler that writes nothing to the request (e.g. does nothing), but keeps Express from handling it.
Or, another possibility would be to call the user's server callback from your express 404 handler and not elsewhere.
Related
I've been using Express for a while but suddenly I'm unsure about something pretty basic --
I'm trying to add custom middleware to a KeystoneJS application -- specifically I'm adding a JWT token endpoint to a TinyMCE custom field
The custom field is
export let Wysiwyg = {
type: 'Wysiwyg',
implementation: Text.implementation,
views: {
Controller: Text.views.Controller,
Field: importView('./views/Field'),
Filter: Text.views.Filter,
},
adapters: Text.adapters,
prepareMiddleware,
};
and prepareMiddleware is
function prepareMiddleware() {
const tinymcePath = dirname(require.resolve('tinymce'));
const app = express();
app.use(cors());
app.use('/tinymce-assets', express.static(tinymcePath));
app.post('/jwt', function (req, res) {
// NOTE: Before you proceed with the TOKEN, verify your users' session or access.
const payload = {
sub: '123', // Unique user id string
name: 'John Doe', // Full name of user
// Optional custom user root path
// 'https://claims.tiny.cloud/drive/root': '/johndoe',
exp: Math.floor(Date.now() / 1000) + (60 * 60) // 60 minutes expiration
};
try {
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256'});
res.set('content-type', 'application/json');
res.status(200);
res.send(JSON.stringify({
token: token
}));
} catch (e) {
res.status(500);
res.send(e.message);
}
});
return app;
}
This is all called from a KeystoneJS app that has its own ExpressJS server running. What exactly is the call to express() above doing? The ExpressJS API docs say
**express()**
Creates an Express application. The express() function is a top-level function exported by the express module.
var express = require('express')
var app = express()
I always understood this to be creating a new HTTP server. Surely you don't want to do that twice in a single app unless you're serving on different ports (which I'm not trying to do)?
Similarly, the KeystoneJS docs say
If you need to provide your own custom middleware for your system you
can create a custom App and include it in your exported apps.
class CustomApp {
prepareMiddleware({ keystone, dev, distDir }) {
const middleware = express();
// ...
return middleware;
}
}
Here, again, they're calling express().
What exactly happens when you callexpress()? It starts a new Express app, according to the docs, but I always thought this was equivalent to starting a new server. Surely, I thought, you can't start two servers on the same port.
Thanks for helping clear up my confusion -- I'm obviously not seeing the forest for the trees.
express() basically just creates a stack of middleware functions. It's not a server on its own.
Because it's just a stack of middleware, an Express app can be 'mounted' into another app. An example is shown here (edited for brevity):
var sub2 = express();
sub2.get("/", (req, res) => { res.json({}); });
var app = express();
app.use("/foo", sub2);
Defining and use()ing a new Express instance is really no different from loading any other middleware stack, such as express.Router().
As for binding to ports, usually, you'll only call the listen() helper function on the upper-most Express app instance. All this does is set up a basic HTTP server (so you don't have to) and registers your Express instance as the callback. It's little different from doing http.createServer(options, myUpperMostExpressApp);.
I am a newbie in node.js, but I have a sample application written for node.js that shows the way of integration with specific Identity Provider (SAML). Using this sample app I am trying to inspect what is sent in the HTTP requests made from the node.js backend to the remote IdP. Logging the request headers and body by writing to the console.log would be enough for me. Monitoring the network traffic with some tool like Fiddler is not an option for me, because I cannot run it locally, I need to have the app exposed and I have it deployed to Heroku.
I've tried morgan, but it only intercepts the INCOMING requests. I've also tried global-request-logger, but for some reason it does not inject itself into the express framework and passport. Seems like passport is not using the standard modules for HTTP requests?
The question is: what I need to use to be able to log the content of the HTTP requests made by passport during the .authenticate() call? Is there any flag that I am able to set in passport to enable HTTP logging? Or should I rather do it in express? Or maybe some other package would provide the functionality I need?
EDIT:
My original question was marked as possible duplicate of how to log OUTGOING https requests from node within webstorm
But actually I have already seen that topic and I've tried to setup a hook to http module, it was done this way:
'use strict';
// Setup express.js application:
var express = require('express');
var app = express();
// Patch "http" module with outgoing request logging:
var http = require('http');
const originalRequest = http.request;
http.request = function wrapMethodRequest(req) {
console.log('EXTERNAL OUTGOING REQUEST LOGGING:');
console.log(req.host, req.body);
return originalRequest.apply(this, arguments);
}
This approach was not working. As already said in the original question, it seems that passport does not use standard http module? Or did I something wrong with the code above?
As already mentioned in the original question, I was also trying to handle it via global-request-logger package (which as explained in the possible duplicated post, uses the same technique). The code for that was:
'use strict';
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
var passport = require('passport');
var SamlStrategy = require('passport-saml').Strategy;
app.use(bodyParser.urlencoded({ extended: false }));
var globalLog = require('global-request-logger');
globalLog.initialize();
globalLog.on('success', (req, res) => {
console.log('HTTP(S) CALLOUT SUCCESS');
console.log('REQUEST: ', req);
console.log('RESPONSE: ', res);
});
globalLog.on('error', (req, res) => {
console.log('HTTP(S) CALLOUT ERROR');
console.log('REQUEST: ', req);
console.log('RESPONSE: ', res);
});
...
I have very basic application at the moment that I wanted to try to implement SSR with.
problem 1
Previously I would send index.html (that had a script tag with my bundle.js inside) to requests made to '/' and that would render on the client.
Now I'm rendering the app on the server for every request made to '/' and now, expectantly, when I make GET requests to '/api/users/' my isomorphic rendering function is executed (even though this is an API request and should not be rendering anything).
I understand why, because of where I placed this function in the middleware is such that all requests made to the app will run that middleware.
My question is: How can I ignore this function unless a browser is requesting it? Is that even the right question? It sounds like I'm asking for a hack, which means I'm misunderstanding something.
problem 2
This is the cause of another problem I am having when the client requests bundle.js. Because each request is rendering the app and sending html back, requests to get the bundle are receiving an HTML response, which when parsed by the browser errors out as expected (Unexpected token '<').
I think I've designed my server poorly, and maybe someone can give me some insight or point me to a good place to start learning.
server.js
require('babel-register')
var express = require('express')
// TODO setup global error handling
var path = require('path') // built-in middleware
var mongoose = require('mongoose')
const isomorphic = require('./isomorphic')
// change promise library of mongoose to bluebird
mongoose.Promise = require('bluebird')
// create the api and auth
var api = require('./api/api')
var auth = require('./auth/routes')
// make app
var app = express()
// setup the app middleware
require('./middleware/appMiddleware')(app)
// get the api and auth
app.use('/api', api)
app.use('/auth', auth)
// serve static assets
app.use(express.static('dist'))
// serverside rendering
app.use(isomorphic)
// give the bundle
// app.get('/dist/bundle.js', function(req, res) {
// res.sendFile(path.resolve('dist/bundle.js'), function(err) {
// if (err) {
// res.status(500).send(err)
// }
// })
// })
module.exports = app
Note app.get('/dist/bundle.js', function... is commented out because I believe app.use(express.static('dist)) should be handling that. Right?
isomorphic.js
var React = require('react')
var ReactDOMServer = require('react-dom/server')
var StaticRouter = require('react-router-dom').StaticRouter
var ServerStyleSheet = require('styled-components').ServerStyleSheet
var fs = require('fs')
var _ = require('lodash')
var baseTemplate = fs.readFileSync('./index.html') // TODO change to async function maybe
var template = _.template(baseTemplate)
var sheet = new ServerStyleSheet()
var something = sheet.collectStyles(App)
var css = sheet.getStyleTags()
var App = require('../src/js/App').default
const isomorphic = (req, res) => {
var context = {}
var body = ReactDOMServer.renderToString(
React.createElement(StaticRouter, { location: req.url, context },
React.createElement(App)
)
)
console.log('hello?');
if (context.url) {
// TODO if there is a redirect
}
res.write(template({body: body}))
res.end()
}
module.exports = isomorphic
Please let me know if you need to see any other files.
update 1
I added a check in isomorphic.js that looks like this:
if (req.url !== '/') {
next()
} else {
...
}
And I uncommented the app.get('/dist/bundle.js') code.
Now everything seems to be half working, however:
That if check seems to be bad practice because the user could request routes that exist on the client that are not the root.
React's server render does match the checksum on the client, therefore negating the SSR.
And the app.use(express.static('dist')) appears to be doing absolutely nothing. The 'dist' directory is one level up from my server.js file, so I've changed it to '../dist' but it still 404s (with that .get commented).
update 2
I have figured out why express.static('dist') was not working.
One, I changed it from express.static('dist') to express.static(path.join(__dirname, '../dist/')) to be sure it going to the correct path.
Two, in my index.html, the script tag was originally
<script src="dist/bundle.js"></script>
when apparently it should have been this (I got lucky and guessed this could have been the issue)
<script src="bundle.js"></script>
I am an intermediate level developer on MEAN Stack, but I wanted to understand this, as to what exactly happens after I press run node app.js. After reading a lot, this is what I found out. Correct me if I am wrong.
Step 1 : All of the JavaScript code written is complied and looked for syntactic errors. If that is error free, the node starts to listen on the mentioned port. By listening I mean in layman languages is, it kinda opens a door where anything could come from that door.
Step 2: All the routes are listed somewhere like these are GET, these are POST request etc. A guard on that door will check the incoming request, and see if it matches any of the routes. If the HTTP header and routes and permissions(CORS) are matched, the guard gives it permission to go and execute that particular route, and then the response is sent back through the same door, with the required fields like status, data etc.
Mid-Step: When the request is being processed, all the IO tasks are given to underlying threads by libev and when they complete, they come back to the event table and then are processed by the event loop.
Is this understanding correct? Would somebody like to add something to it. Probably something that I am missing.
EDIT
/*Writing var once and declaring all dependencies at once is considered a
good practice.*/
var express = require('express'),
cookieParser = require('cookie-parser'),
bodyParser = require('body-parser'),
mongoose = require('mongoose'),
passport = require('passport'),
session = require('express-session');
var app = express();
var server = require('http').Server(app);
var io = require('socket.io')(server);
/*All my confidential data, keys go in here*/
require('./config/credentials.js').db(app, mongoose);
require('./config/environment.js')(app, express, bodyParser, cookieParser, session, passport, mongoose);
/*Models*/
Users = require('./models/users.js');
var checkauth = function(req, res, next) {
console.log("In checkauth");
console.log(req.user);
if (req.isAuthenticated()) {
console.log("In isAuthenticated");
next();
} else {
console.log("Doesn't authenticate");
res.status(401);
res.set('Content-Type', 'application/json');
res.end(JSON.stringify({
'success': false
}));
}
};
/*Routes to all my files*/
require('./routes/index.js')(app, express, io);
require('./routes/vehico.js')(app);
/*Mobile Routes.*/
require('./routes/mobile/auth.js')(app, passport, mongoose);
require('./routes/mobile/settings.js')(app, checkauth);
require('./routes/mobile/cars.js')(app, checkauth);
require('./routes/mobile/devices.js')(app, checkauth);
require('./routes/mobile/locations.js')(app, checkauth);
require('./routes/mobile/group.js')(app, checkauth);
require('./routes/mobile/analytics.js')(app, checkauth);
/* Device Route */
require('./routes/device.js')(app, io);
require('./routes/trip.js')(app, checkauth);
require('./routes/mobile.js')(app, express, checkauth);
app.listen(app.get('port')); // port should be in config folder in a file not out here in open.
console.log('Express server listening on port 4000');
From koajs.com:
app.callback()
Return a callback function suitable for the http.createServer() method to handle a request. You may also use this callback function to mount your koa app in a Connect/Express app.
Now I have an Express app that already starts its own http server. How can I mount a koa app on top of this existing server, so that it shares the same port?
Would I include the koa app as an Express middlware? Do I still use app.callback() for that?
expressapp.use(koaapp.callback()) is fine. but remember, koaapp.callback() does not have a next, so there's no passing errors to the express app or skipping the koaapp once you use it.
it's better to keep them completely separate since their APIs are incompatible
var koaapp = koa()
var expressapp = express()
http.createServer(req, res) {
if (true) koaapp(req, res);
else expressapp(req, res);
})
Since you need a server instance in order to mount a middleware on a specific /prefix, it would be something like
var http = require('http');
var expressApp = require('express');
var koaApp = require('koa');
// ...
expressApp.use('/prefix', http.createServer(koaApp.callback()));