SSR场景下如何获取主机名?跨域API域名适配问题
解决SSR环境下动态设置API域名的问题
这个场景我太熟悉了——做跨域名的前后端分离项目,本地和线上要自动切换API地址,客户端用window没问题,但一到SSR就直接报错,核心原因就是服务端压根没有window这个浏览器对象,得从服务端的请求上下文里拿主机信息才行。
下面分通用方案和主流SSR框架的具体实现来给你讲:
通用思路:区分客户端/服务端环境,分别获取主机信息
我们可以给Domain类加个判断逻辑,客户端继续用window.location,服务端则从请求对象的Host头里提取主机名:
export abstract class Domain { // 客户端判断是否为本地环境 private static isClientLocal(): boolean { return typeof window !== 'undefined' && window.location.hostname === 'localhost'; } // 服务端通过请求对象判断是否为本地环境 private static isServerLocal(req: any): boolean { const host = req.headers.host || ''; // 匹配localhost开头的主机,比如localhost:5000 return host.startsWith('localhost'); } // 对外暴露的方法,服务端调用时必须传req对象 public static getApiDomain(req?: any): string { // 先判断是不是客户端环境 if (typeof window !== 'undefined') { return this.isClientLocal() ? "http://localhost:5001" : "http://api.domain.com"; } // 服务端环境必须传req,不然直接抛错提示 if (!req) { throw new Error('请在服务端调用时传入请求对象(req)'); } return this.isServerLocal(req) ? "http://localhost:5001" : "http://api.domain.com"; } }
然后在服务端渲染的入口里,比如Express的路由处理函数,调用的时候把req传进去:
// 举个Express SSR的例子 app.get('*', (req, res) => { const apiDomain = Domain.getApiDomain(req); // 用这个apiDomain去请求数据,再渲染页面 });
主流SSR框架的简化实现
如果你用的是Next.js、Nuxt.js这类框架,它们已经帮你封装好了请求上下文,直接拿就行:
Next.js
在getServerSideProps或者API路由里,直接从context里取req:
export async function getServerSideProps(context) { const apiDomain = Domain.getApiDomain(context.req); const apiRes = await fetch(`${apiDomain}/your-endpoint`); const data = await apiRes.json(); return { props: { data } }; }
Nuxt.js
在asyncData或者serverMiddleware里,通过context.req获取请求对象:
export default { async asyncData(context) { const apiDomain = Domain.getApiDomain(context.req); const data = await this.$axios.$get(`${apiDomain}/your-endpoint`); return { data }; } }
额外小技巧:用环境变量简化判断
如果你的本地和线上环境是通过NODE_ENV区分的,其实可以不用判断主机名,直接用环境变量更靠谱:
// 本地开发时设置NODE_ENV=development,线上设为production export abstract class Domain { public static getApiDomain(): string { return process.env.NODE_ENV === 'development' ? "http://localhost:5001" : "http://api.domain.com"; } }
这种方式代码更简洁,除非你有多个测试域名需要区分,否则优先用这个方案。
内容的提问来源于stack exchange,提问作者Aleksandr Albert
相关产品推荐
相关产品推荐

