我有一个使用 Angular SSR(express)的 Spartacus/SAP Composable Storefront 应用程序。我本质上想做的是使用 SSR 执行 301 重定向,其中替换 URL 旨在通过调用外部 API 来检索替换 URL 的必要部分来动态组装。
在服务类中,我使用
@Inject(RESPONSE)
注入了 SSR 的 Response 对象。此外,在同一服务中,URL 重定向逻辑是在 HTTP 客户端调用前面提到的 API 返回的 Observable 的订阅块中完成的。在订阅块中,我将 SSR 的响应对象的 HTTP 状态设置为 301,并将其位置设置为新组装的替换 URL。
一旦代码到达“server.ts”片段中
res.render
的回调块(如下所示),res.redirect
将被调用。
server.get('*', (req, res) => {
res.render(indexHtml, {
req,
providers: [{provide: APP_BASE_HREF, useValue: req.baseUrl}],
}, (err, html) => {
if (res.statusCode === 301 || res.statusCode === 302) {
res.redirect(res.statusCode, (res.header as any).REDIRECT_URL)
}
//more logic here
});
});
当流量不多时,所有这些都运行良好。然而,当我们的 Spartacus 应用程序有大量并发请求时,我意识到在“server.ts”中,“req”有时可能会与对同一应用程序发出的另一个请求中的完全不相关的“res”配对。因此,导致错误的 301 重定向。
起初我怀疑这可能是一个异步问题(竞争条件),因此我需要在解决提到的 Observable 之前阻止 SSR 渲染。因此,我尝试实现一个 Angular 解析器,并将外部 HTTP 调用放在
resolve
方法中。然后,我通过订阅 ngOnInit
的数据来访问组装替换 URL 所需的数据,从而在相应组件的 ActivatedRoute
方法中实现了 SSR 响应对象的状态和位置的更新。然而,我仍然看到同样的问题,即“res”和“req”会间歇性地不匹配..
我想知道什么可能导致这个问题?它可能仍然是一个异步(竞争条件)问题,因为我可能没有正确实现 Angular 解析器(我对 Angular 和 FE 开发还很陌生),或者这是一个并发问题,来自另一个请求的响应会干扰来自另一个线程/会话的请求。
我非常感谢在这个问题上的一些帮助和指导,因为我已经被困了很长一段时间了..
事实证明,我最好直接在 server.ts 中进行外部 API 调用,而不是依赖 Angular 的服务。老实说,我无法查明为什么会发生这些竞争条件的确切问题。
从这个问题来看,它证明在 Angular 中写入 SSR 响应或请求对象并不是一个好主意,因为它很容易出现此类竞争条件(由于缺乏经验,我的说法可能是错误的) Angular 和 FE 开发)。
至于下面的解决方案,我已将
res.redirect
从 res.render
的回调中移出。在这种情况下,这是有道理的,因为如果初始请求 URL 一开始就不正确,我根本不希望页面被渲染。
虽然这里的缺点是,对于我的 SSR 路由拦截的每个请求,SSR 服务器都必须执行外部 API 调用来决定是否应该重定向或继续像往常一样渲染页面。
const axios = require('axios')
server.get('/web/nonCanonicalItemName/:itemCode', async(req, res) => {
let itemCode = req.params.itemCode;
try {
const response = await axios.get(`https://api.example/items/${itemCode}`, {
headers: {
'someHeader': 'foo'
}
})
const item = response.data;
let replacementURL = `/web/${item.canonicalItemName}/p/${itemCode}`;
if (req.url !== replacementURL) {
res.redirect(301, replacementURL);
} else {
res.render(indexHtml, {
req,
providers: [{
provide: APP_BASE_HREF,
useValue: req.baseUrl
}],
}, (err, html) => {
//some more custom logic here
res.send(html);
});
}
} catch (error) {
res.status(500).json({
error: 'An error has occurred'
});
}
})