reactjs – 服务器呈现泄露用户的Redux存储数据的React ExpressJ
发布时间:2020-12-15 20:11:55 所属栏目:百科 来源:网络整理
导读:我有一个ExpressJS服务器,有时在初始渲染时呈现错误的用户数据.请参阅下面的(稍微简化)版本. 问题是index.ejs文件经常在reduxState中呈现错误的用户数据… 我的困惑是因为我希望从’routes.js’导入{store}的调用以每个用户的请求覆盖商店{}.问题似乎是商店
我有一个ExpressJS服务器,有时在初始渲染时呈现错误的用户数据.请参阅下面的(稍微简化)版本.
问题是index.ejs文件经常在reduxState中呈现错误的用户数据… 我的困惑是因为我希望从’routes.js’导入{store}的调用以每个用户的请求覆盖商店{}.问题似乎是商店正在成为网站上每个用户组合在一起的组合商店. 如何确保每个用户只能看到他们在网站上的数据? routes.js // src/routes.js import React from 'react'; import { createStore,applyMiddleware,compose } from "redux"; import routerConfig from "base/routes/routes"; import thunk from "redux-thunk"; import { rootReducer } from "base/reducers"; let initialState = {}; const store = createStore( rootReducer,initialState,compose(applyMiddleware(thunk)) ); const routes = routerConfig(store); export {store}; export default routes; server.js import { store } from 'routes'; let getReduxPromise = (renderProps,request) => { let store = require('./routes/index.jsx').store let { query,params } = renderProps let comp = renderProps.components[renderProps.components.length - 1]; let at = null; if (request && request.cookies && request.cookies.accessToken) { at = request.cookies.accessToken } if (comp.fetchData) { return comp.fetchData({ query,params,store,at }).then(response => { if (request) { if (request.cookies && request.cookies.accessToken && request.cookies.userInfo) { store.dispatch( actions.auth(request.cookies.userInfo),request.cookies.accessToken ) } else { store.dispatch(actions.logout()) } } return Promise.resolve({response,state: store.getState()}) }); } else { return Promise.resolve(); } } app.get('*',(request,response) => { let htmlFilePath = path.resolve('build/index.html' ); // let htmlFilePath = path.join(__dirname,'/build','index.html'); let error = () => response.status(404).send('404 - Page not found'); fs.readFile(htmlFilePath,'utf8',(err,htmlData) => { if (err) { console.log('error 1') error(); } else { match({routes,location: request.url},redirect,renderProps) => { if (err) { console.log('error 2') error(); } else if (redirect) { return response.redirect(302,redirect.pathname + redirect.search) } else if (renderProps) { let parseUrl = request.url.split('/'); if (request.url.startsWith('/')) { parseUrl = request.url.replace('/','').split('/'); } // User has a cookie,use this to help figure out where to send them. if (request.cookies.userInfo) { const userInfo = request.cookies.userInfo if (parseUrl[0] && parseUrl[0] === 'profile' && userInfo) { // Redirect the user to their proper profile. if (renderProps.params['id'].toString() !== userInfo.id.toString()) { parseUrl[1] = userInfo.id.toString(); const url = '/' + parseUrl.join('/'); return response.redirect(url); } } } getReduxPromise(renderProps,request).then((initialData) => { let generatedContent = initialData.response ? render(request,renderProps,initialData.response) : render(request,{}); const title = initialData.response.SEO.title || ''; const description = initialData.response.SEO.description || ''; var draft = []; const currentState = initialData.state; if (currentState) { const reduxState = JSON.stringify(currentState,function(key,value) { if (typeof value === 'object' && value !== null) { if (draft.indexOf(value) !== -1) { // Circular reference found,discard key return; } // Store value in our collection draft.push(value); } return value; }); draft = null; ejs.renderFile( path.resolve('./src/index.ejs' ),{ jsFile,cssFile,production,generatedContent,reduxState,title,description },{},function(err,str) { if (err) { console.log('error 3') console.log(err); } response.status(200).send(str); }); } else { console.log('error 4') console.log(err) error(); } }).catch(err => { console.log('error 5') console.log(err) error(); }); } else { console.log('error 6') console.log(err) error(); } }); } }) }); index.ejs <!DOCTYPE html> <html lang="en"> <head> <title><%- title %></title> <meta name="description" content="<%- description %>"/> <link href="<%- cssFile %>" rel="stylesheet"/> <script type="text/javascript" charset="utf-8"> window.__REDUX_STATE__ = <%- reduxState %>; </script> </head> <body> <div id="root"><%- generatedContent %></div> <script type="text/javascript" src="<%- jsFile %>" defer></script> </body> </html> React组件中的示例fetchData函数 ExamplePage.fetchData = function (options) { const { store,at } = options return Promise.all([ store.dispatch(exampleAction(params.id,ACTION_TYPE,userAccessToken)) ]).spread(() => { let data = { SEO: { title: 'SEO Title',description: 'SEO Description' } } return Promise.resolve(data) }) } 解决方法
模块范围中定义的变量在整个运行时环境中只有一个副本.这意味着每个node.js进程都有自己的副本,每个浏览器选项卡/框架都有自己的副本.但是,在每个选项卡或每个进程中,只有一个副本.这意味着您无法将商店定义为模块级const,并且仍然为每个用??户创建一个新商店.你可以这样解决:
SRC / routes.js import React from 'react'; import { createStore,compose } from "redux"; import routerConfig from "base/routes/routes"; import thunk from "redux-thunk"; import { rootReducer } from "base/reducers"; let initialState = {}; export function newUserEnv() { const store = createStore( rootReducer,compose(applyMiddleware(thunk)) ); const routes = routerConfig(store); return { store,routes }; } server.js import { newUserEnv } from 'routes'; let getReduxPromise = (renderProps,request) => { const { store } = newUserEnv(); let { query,params } = renderProps ... 这为每个请求创建了一个新商店,并允许每个用户拥有自己的数据.请注意,如果您需要来自不同模块的相同商店,则需要传递它.您不能只导入{newUserEnv},因为它会创建一个新的. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |