间歇性不正确的 SSR 重定向(server.ts 级别的请求和响应不匹配)

问题描述 投票:0回答:1

我有一个使用 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 开发还很陌生),或者这是一个并发问题,来自另一个请求的响应会干扰来自另一个线程/会话的请求。

我非常感谢在这个问题上的一些帮助和指导,因为我已经被困了很长一段时间了..

angular express server-side-rendering race-condition spartacus-storefront
1个回答
0
投票

事实证明,我最好直接在 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'
    });
  }
})

© www.soinside.com 2019 - 2024. All rights reserved.