Я использую mern.io для своего приложения вместе с Passport и express-сессиями. Для каждого запроса deserializeUser вызывается дважды для одного и того же запроса. Это не проблема с express.static или express.favicon, как другие пользователи указали в качестве возможных виновников. Первый раз, когда он называется req.user, не определен. При втором вызове req.user верен. UserObject в deserializeUser корректен в обоих случаях:
{ userId: '1234',
firstName: 'Joe',
lastName: 'Smith',
email: 'asdf@aol.com' }
Вот мой server.js:
<code>import Express from 'express';
import compression from 'compression';
import bodyParser from 'body-parser';
import path from 'path';
const passport = require('passport');
const cookieParser = require('cookie-parser');
const expressSession = require('express-session');
const MongoStore = require('connect-mongo')(expressSession);
// Webpack Requirements
import webpack from 'webpack';
import config from '../webpack.config.dev';
import webpackDevMiddleware from 'webpack-dev-middleware';
import webpackHotMiddleware from 'webpack-hot-middleware';
// Initialize the Express App
const app = new Express();
// Set Development modes checks
const isDevMode = process.env.NODE_ENV === 'development' || false;
const isProdMode = process.env.NODE_ENV === 'production' || false;
// Run Webpack dev server in development mode
if (isDevMode) {
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath:
config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
}
// React And Redux Setup
import { configureStore } from '../client/store';
import { Provider } from 'react-redux';
import React from 'react';
import { renderToString } from 'react-dom/server';
import { match, RouterContext } from 'react-router';
import Helmet from 'react-helmet';
import routes from '../client/routes';
import { fetchComponentData } from './util/fetchData';
import serverConfig from './config';
import serverRoutes from './routes/routes';
app.use(compression());
app.use(Express.static(path.resolve(__dirname, '../dist/client')));
app.use(cookieParser('somethingsomething'));
app.use(bodyParser.json({ limit: '20mb' }));
app.use(bodyParser.urlencoded({ limit: '20mb', extended: false }));
require('./passportConfig')(passport);
app.use(expressSession({
name: 'testApp',
secret: 'somethingsomething',
resave: true,
saveUninitialized: false,
store: new MongoStore({ url: process.env.DBURL }),
cookie: {
maxAge: 36000 * 24 * 14,
secure: false,
},
}));
app.use(passport.initialize());
app.use(passport.session());
app.use((req, res, next) => {
res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept');
next();
});
app.use('/api', serverRoutes);
// Render Initial HTML
const renderFullPage = (html, initialState) => {
const head = Helmet.rewind();
// Import Manifests
const assetsManifest = process.env.webpackAssets && JSON.parse(process.env.webpackAssets);
const chunkManifest = process.env.webpackChunkAssets && JSON.parse(process.env.webpackChunkAssets);
return `
<!doctype html>
<html>
<head>
${head.base.toString()}
${head.title.toString()}
${head.meta.toString()}
${head.link.toString()}
${head.script.toString()}
${isProdMode ? `<link rel='stylesheet' href='${assetsManifest['/app.css']}' />` : ''}
<link href='https://fonts.googleapis.com/css?family=Lato:400,300,700' rel='stylesheet' type='text/css'/>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.2.12/semantic.min.css"></link>
<link rel="shortcut icon" href="http://res.cloudinary.com/hashnode/image/upload/v1455629445/static_imgs/mern/mern-favicon-circle-fill.png" type="image/png" />
</head>
<body>
<div id="root"><div>${html}</div></div>
<script>
window.__INITIAL_STATE__ = ${JSON.stringify(initialState)};
${isProdMode ?
`//<![CDATA[
window.webpackManifest = ${JSON.stringify(chunkManifest)};
//]]>` : ''}
</script>
<script src='${isProdMode ? assetsManifest['/vendor.js'] : '/vendor.js'}'></script>
<script src='${isProdMode ? assetsManifest['/app.js'] : '/app.js'}'></script>
</body>
</html>
`;
};
const renderError = err => {
const softTab = '    ';
const errTrace = isProdMode ?
`:<br><br><pre style="color:red">${softTab}${err.stack.replace(/\n/g, `<br>${softTab}`)}
`: '';
return renderFullPage (`Ошибка сервера $ {errTrace}`, {});
};
// Рендеринг на стороне сервера на основе маршрутов, согласованных с React-router.
app.use ((req, res, next) => {
match ({маршруты, местоположение: req.url}, (ошибка, redirectLocation, renderProps) => {
if (err) {
return res.status (500) .end (renderError (err));
}
if (redirectLocation) {
return res.redirect (302, redirectLocation.pathname + redirectLocation.search);
}
if (! renderProps) {
вернуться далее ();
}
const store = configureStore ();
вернуть fetchComponentData (store, renderProps.components, renderProps.params)
.then (() => {
const initialView = renderToString (
);
const finalState = store.getState ();
Рез
.set ('Content-Type', 'text / html')
.status (200)
.end (renderFullPage (initialView, finalState));
})
.catch ((ошибка) => следующий (ошибка));
});
});
// запустить приложение
app.listen (serverConfig.port, (error) => {
if (! error) {
console.log (`MERN работает на порту: $ {serverConfig.port}! Создайте что-нибудь удивительное!`); // eslint-disable-line
}
});
экспорт приложения по умолчанию;
Вот мой passportConfig.js:
const LocalStrategy = require('passport-local').Strategy;
const request = require('request');
const apiUrl = process.env.API_URL;
const token = process.env.TOKEN;
const User = require('./models/user');
import Config from './config';
const API_URL = (typeof window === 'undefined' || process.env.NODE_ENV === 'test') ?
process.env.BASE_URL || (`http://localhost:${process.env.PORT || Config.port}/api`) : '/api';
module.exports = (passport) => {
passport.serializeUser((userObject, done) => {
done(null, userObject);
});
passport.deserializeUser((userObject, done) => {
if (userObject.adminUser) done(null, userObject.adminUser);
else done(null, userObject);
});
passport.use('local-login', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true, // allows us to pass back the entire request to the callback
}, (req, email, password, done) => {
const proj = {
firstName: 1,
lastName: 1,
email: 1,
};
User.findOneAsync({ email }, proj)
.then(user => {
const userData = {
userId: user._id,
firstName: user.firstName,
lastName: user.lastName,
email: user.email,
};
return done(null, userData);
});
})
);
passport.use('signUp', new LocalStrategy({
usernameField: 'email',
passwordField: 'password',
passReqToCallback: true,
}, (req, email, password, done) => {
request({
url: `${API_URL}/signIn`,
method: 'POST',
json: {
email,
password,
},
'Content-Type': 'application/json',
Accept: 'application/json',
headers: {
Authorization: token,
},
}, (err, resp, body) => {
const respBody = body || {};
if (err) return done(err);
if (!respBody.email) return done(null, false, { message: respBody.err });
return done(null, { user: respBody });
});
}));
};
Кто-нибудь знает, что может быть причиной этого?