如何在同构 React 应用程序的 server-side 数据获取中访问 cookie
How to access cookie in server-side data fetch for isomorphic React app
我正在构建一个 isomorphic/universal React + Redux + Express 应用程序。我的 server-side 数据获取非常标准:我看到哪些路由匹配 URL,调用所有 return 承诺的相关 data-fetching 方法,然后等待它们解析并呈现HTML.
我的挑战:一些 API 调用需要授权。 Logged-in 用户有一个 cookie,当然会随每个请求一起发送。但是当我在服务器端获取数据以填充存储以进行初始渲染时,cookie 对我的 API 调用不可用。我如何做到这一点?
// server-entry.jsx
app.get('*', (req, res) => {
const store = createStore(
combineReducers({
// lots of reducers
}),
{},
applyMiddleware(thunk),
);
/*
The contents of getDataFetchers isn't important. All you need
to know is it returns an array of data-fetching promises like so:
dispatch(thunkAction());
*/
const fetchers = getDataFetchers(req.url, store);
Promise.all(fetchers).then(() => {
// render the tree
});
});
// one of my thunk actions hits an API endpoint looking like this:
app.get('/api', (req, res) => {
// this is simplified, but you get the idea:
// we need access to the cookie to authenticate
// but when this call is made server-side, req.session.user doesn't exist
if (!req.session.user) res.status(403);
else res.json({ data: 'here' });
});
我想我可以从 app.get
中的 req
获取 cookie,并将它作为参数一直传递到实际数据获取(通过 axios 完成),其中它将用 Cookie
header 指定。但这感觉很恶心。
您的建议是正确的,如果您希望 server-side HTTP 请求包含原始请求的 cookie,则需要以允许它的方式构建 HTTP 客户端(在您的情况下为 axios)在实例化时或发出每个请求之前访问 headers。
Here is a link to how Apollo does it.
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import Express from 'express';
import { StaticRouter } from 'react-router';
import { InMemoryCache } from "apollo-cache-inmemory";
import Layout from './routes/Layout';
// Note you don't have to use any particular http server, but
// we're using Express in this example
const app = new Express();
app.use((req, res) => {
const client = new ApolloClient({
ssrMode: true,
// Remember that this is the interface the SSR server will use to connect to the
// API server, so we need to ensure it isn't firewalled, etc
link: createHttpLink({
uri: 'http://localhost:3010',
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
}),
cache: new InMemoryCache(),
});
const context = {};
// The client-side App will instead use <BrowserRouter>
const App = (
<ApolloProvider client={client}>
<StaticRouter location={req.url} context={context}>
<Layout />
</StaticRouter>
</ApolloProvider>
);
// rendering code (see below)
});
如您所见,ApolloClient
已创建并在实例化时传递了初始请求的 cookie。
我正在构建一个 isomorphic/universal React + Redux + Express 应用程序。我的 server-side 数据获取非常标准:我看到哪些路由匹配 URL,调用所有 return 承诺的相关 data-fetching 方法,然后等待它们解析并呈现HTML.
我的挑战:一些 API 调用需要授权。 Logged-in 用户有一个 cookie,当然会随每个请求一起发送。但是当我在服务器端获取数据以填充存储以进行初始渲染时,cookie 对我的 API 调用不可用。我如何做到这一点?
// server-entry.jsx
app.get('*', (req, res) => {
const store = createStore(
combineReducers({
// lots of reducers
}),
{},
applyMiddleware(thunk),
);
/*
The contents of getDataFetchers isn't important. All you need
to know is it returns an array of data-fetching promises like so:
dispatch(thunkAction());
*/
const fetchers = getDataFetchers(req.url, store);
Promise.all(fetchers).then(() => {
// render the tree
});
});
// one of my thunk actions hits an API endpoint looking like this:
app.get('/api', (req, res) => {
// this is simplified, but you get the idea:
// we need access to the cookie to authenticate
// but when this call is made server-side, req.session.user doesn't exist
if (!req.session.user) res.status(403);
else res.json({ data: 'here' });
});
我想我可以从 app.get
中的 req
获取 cookie,并将它作为参数一直传递到实际数据获取(通过 axios 完成),其中它将用 Cookie
header 指定。但这感觉很恶心。
您的建议是正确的,如果您希望 server-side HTTP 请求包含原始请求的 cookie,则需要以允许它的方式构建 HTTP 客户端(在您的情况下为 axios)在实例化时或发出每个请求之前访问 headers。
Here is a link to how Apollo does it.
import { ApolloProvider } from 'react-apollo';
import { ApolloClient } from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import Express from 'express';
import { StaticRouter } from 'react-router';
import { InMemoryCache } from "apollo-cache-inmemory";
import Layout from './routes/Layout';
// Note you don't have to use any particular http server, but
// we're using Express in this example
const app = new Express();
app.use((req, res) => {
const client = new ApolloClient({
ssrMode: true,
// Remember that this is the interface the SSR server will use to connect to the
// API server, so we need to ensure it isn't firewalled, etc
link: createHttpLink({
uri: 'http://localhost:3010',
credentials: 'same-origin',
headers: {
cookie: req.header('Cookie'),
},
}),
cache: new InMemoryCache(),
});
const context = {};
// The client-side App will instead use <BrowserRouter>
const App = (
<ApolloProvider client={client}>
<StaticRouter location={req.url} context={context}>
<Layout />
</StaticRouter>
</ApolloProvider>
);
// rendering code (see below)
});
如您所见,ApolloClient
已创建并在实例化时传递了初始请求的 cookie。