我有一个Spartacus/SAP Composable Storefront应用程序,使用Angular SSR(Express).我实际上试图做的是用SSR执行301重定向,其中替换URL是通过调用外部API来动态组装的,以检索替换URL的必要部分.

在一个服务类中,我给SSR的响应对象注入了@Inject(RESPONSE).此外,在同一个服务中,URL重定向逻辑在HTTP客户端调用前面提到的API返回的Observable的subscribe块中完成.在subscribe块中,我将把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重定向.

起初,我怀疑这可能是一个Escc问题(种族条件),我需要阻止SSR渲染之前提到的可观察是解决.因此,我try 实现一个Angular解析器,并将外部HTTP调用放在resolve方法中.然后,通过订阅ActivatedRoute的数据来访问组装替换URL所需的数据,我实现了相应组件的ngOnInit方法中SSR响应对象的状态和位置的更新.然而,我仍然看到同样的问题,即'res'和'req'将间歇性地得到不匹配.

我想知道是什么可能导致这个问题?它可能仍然是一个竞争条件问题,因为我可能没有正确实现Angular解析器(我对Angular和FE开发非常陌生),或者这是一个并发问题,即另一个请求的响应干扰了另一个线程/会话的请求.

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

推荐答案

事实证明,我最好直接在server.ts中进行外部API调用,而不是依赖Angular的服务.老实说,我不能指出确切的问题,为什么这些比赛条件发生.

从这个问题来看,它证明了在Angular内写入SSR响应或请求对象不是一个好主意,因为它容易出现这样的竞争条件(由于缺乏Angular和FE开发的经验,我在这个声明中可能是错误的).

至于下面的解决方案,我已经将res.redirectres.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'
    });
  }
})

Angular相关问答推荐

Angular ngModel双向数据绑定不适用于表单中的Select元素

如何避免使用CSS动画制作的幻灯片中闪烁?

Angular Signal只指一种类型,但这里用作值.ts(2693)''

仅在从forkJoin获得数据后订阅主题

笔刷Angular 为17

Angular router-outlet 未渲染

如何解决在StackBlitz中使用SPARTIER时出现无法解析解析器错误(&C)?

如何将表单作为Angular 分量输入传递?

相同的 Angular 代码在生产环境中停止工作

Angular 13 - 使用 PUT 时出现 400 错误(错误请求)

如何以Angular 改变环境

RxJS 基于先前的数据响应执行请求(涉及循环对象数组)

当访问令牌在 MSAL for Angular 中过期时如何重定向用户?

当我ng serve时,Angular CLI 提示TypeError: callbacks[i] is not a function

使用 rxjs 处理刷新令牌

Angular 没有可用于依赖类型的模块工厂:ContextElementDependency

即使 withCredentials 为真,Angular 也不会发送在 Set-Cookie 中收到的 Cookie

Angular2 + Jspm.io:使用类decorator 时需要反射元数据垫片

Angular 2:将外部js文件导入组件

无法绑定到 target,因为它不是div的已知属性