首页 > 解决方案 > Heroku 上将 Angular 调用代理到后端 API 的正确方法是什么?

问题描述

注:2019 年 6 月 4 日更新;见下文

这不是关于特定代码片段的问题。相反,我正在寻求帮助,为使用 MEAN 堆栈(MongoDB、Express、Angular、Node)与后端 API 通信的 Angular 客户端正确构建代理。我是一个老派的 C 程序员,自学 MEAN。老狗,新把戏。我已经清理了几个资源,包括 Stack Overflow,但我还没有找到答案。Stack Overflow 上的几个用户报告了同样的问题(我在 SOURCES 下列出了这些问题)。我还有一张 Heroku 的帮助票。

感谢所有阅读并考虑它的人。额外感谢(提前)所有发布有用建议和解决方案的人。

问题:您如何正确构建 Angular 客户端对后端 API 的调用以避免 CORS(跨源资源共享)中固有的问题?我从 Angular 文档、代理中绘制和开发的解决方案在我的开发系统(即 localhost)上运行时可以工作,但在上传到我的 PaaS(Heroku)时会失败。这是因为 Angular 的代理只是用于开发的工具吗?如果是这样,调用后端 API 的正确方法是什么?

配置:我正在 Darwin OS (macOS 10.14.4) 上开发,使用 Node 10.13.0、npm 6.9.0、Express 4.16.2 和 Angular 7.2.9。我在 Bitbucket 上使用了一个 git 存储库,它通过管道将部署部署到 Heroku。我对客户端(Angular)和 API(Node/Express)进行了单独的部署。现在客户端运行在 Hobby dyno 上,API 在 Heroku 上的 Free dyno 上运行。我的数据库托管在沙盒(免费数据库)中的 mLab 上。客户端: https ://www.markwilx.com API: http: //markwilx-api.herokuapp.com/api/test

讨论: Stack Overflow 中的以下问题与我遇到的问题相同: 调用 API 的 Angular proxy.conf.json 在本地工作,但在 Heroku 上没有 没有给出解决方案,但 Massimiliano Sartoretto 发表评论说:“那个代理是应该只适用于开发服务器。它与 Heroku 无关,甚至不应该部署在 Heroku 上。它的目标是帮助您在从 localhost 为应用程序提供服务时代理外部 API 调用”我对此评论有疑问。官方的 Angular 文档(参见下面的 SOURCES)没有提及这一点。我没有质疑萨托雷托先生的说法,但我肯定断言他的说法并不广为人知,也没有记录在案。如果有人知道这个事实,我会很感激我可以阅读它的参考。

同样,使用相同方法的 Chenkie 先生在他的书中也没有提到这一点(见下文来源)。几个月来,我多次尝试通过他的网站询问陈凯先生,但他没有回应。当/如果他这样做,我会更新这篇文章。

Heroku 的优秀团队已经回复了我的求助单。Heroku 不提供本机代理功能,因此任何代理都必须在 Angular 客户端中处理。他们倾向于相信问题出在萨托雷托先生所声称的某个地方。当然,描述我应该如何构建我的客户端和 API 超出了他们的服务范围。

最后,我在 Heroku 上使用 Node.js 的 Stack Overflow 代理服务器上找到了以下内容, 这又是我遇到的同样问题。提出问题的人 Andrea Reginato 没有使用 Angular,但他发布了针对 Node.js 的解决方法。我想知道这是否是首选方法,我只需要将它移植到 Angular 中。

我真的被困住了,我想确保我使用行业最佳实践技术构建我的系统。

来源:有关将代理配置到后端服务器的官方 Angular 文档在此链接中提供: https ://angular.io/guide/build#proxying-to-a-backend-server

我一直在阅读和关注 Ryan Chenkie 的“保护 Angular 应用程序”。从第 41 页开始,他开始演示如何为他的示例应用程序构建代理。他的示例与上述 Angular 文档中的方法一致。

以下是与我遇到的问题密切相关的 Stack Overflow 上未解决的查询:

[我不得不删除所有这些。包括它们,Stack Overflow 认为我的帖子是垃圾邮件。我把最重要的放在上面的文字中。]


更新:2019 年 6 月 4 日

我继续研究这个问题,但收效甚微。尽管除了 Massimiliano Sartoretto 的评论指出代理仅用于开发服务器之外,我没有发现任何内容,但我已经删除了代理并试图通过其他方式解决外部 API。

目前,我已经配置了内容安全策略来解决跨域资源共享问题,并且我正在使用 Express 使用以下代码重定向 API 调用:

    app.get('/api/test', function(req, res) {
      request.get({
        url: 'https://markwilx-api.herokuapp.com/api/test' 
      }, function(error, response, body) {
        if(!error && response.statusCode == 200) {
          res.send(body);
        }
      });
    });

再一次,这在我的开发环境(在 localhost 上运行的前端服务器和在 Heroku 上运行的后端服务器)无缝运行,但是一旦上传到 Heroku 就会失败。

在 Heroku 上运行的前端服务器在调用 API 时会出现以下错误:

Jun 04 05:08:02 markwilx heroku/router: at=info method=GET path="/api/test" host=www.markwilx.com request_id=2f789dad-7161-4269-bf92-64db8060eadd fwd="184.170.243.167" dyno=web.1 connect=0ms service=10ms status=500 bytes=404 protocol=https 
Jun 04 05:08:02 markwilx app/web.1: ReferenceError: request is not defined 
Jun 04 05:08:02 markwilx app/web.1:     at /app/server.js:35:3 
Jun 04 05:08:02 markwilx app/web.1:     at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
Jun 04 05:08:02 markwilx app/web.1:     at next (/app/node_modules/express/lib/router/route.js:137:13) 
Jun 04 05:08:02 markwilx app/web.1:     at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3) 
Jun 04 05:08:02 markwilx app/web.1:     at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
Jun 04 05:08:02 markwilx app/web.1:     at /app/node_modules/express/lib/router/index.js:281:22 
Jun 04 05:08:02 markwilx app/web.1:     at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12) 
Jun 04 05:08:02 markwilx app/web.1:     at next (/app/node_modules/express/lib/router/index.js:275:10) 
Jun 04 05:08:02 markwilx app/web.1:     at SendStream.error (/app/node_modules/serve-static/index.js:121:7) 
Jun 04 05:08:02 markwilx app/web.1:     at emitOne (events.js:116:13) 
Jun 04 05:08:07 markwilx heroku/router: at=info method=GET path="/api/test" host=www.markwilx.com request_id=99922c46-a529-4f7e-8190-670cedf3a33d fwd="184.170.243.167" dyno=web.1 connect=0ms service=5ms status=500 bytes=404 protocol=https 
Jun 04 05:08:07 markwilx app/web.1: ReferenceError: request is not defined 
Jun 04 05:08:07 markwilx app/web.1:     at /app/server.js:35:3 
Jun 04 05:08:07 markwilx app/web.1:     at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
Jun 04 05:08:07 markwilx app/web.1:     at next (/app/node_modules/express/lib/router/route.js:137:13) 
Jun 04 05:08:07 markwilx app/web.1:     at Route.dispatch (/app/node_modules/express/lib/router/route.js:112:3) 
Jun 04 05:08:07 markwilx app/web.1:     at Layer.handle [as handle_request] (/app/node_modules/express/lib/router/layer.js:95:5) 
Jun 04 05:08:07 markwilx app/web.1:     at /app/node_modules/express/lib/router/index.js:281:22 
Jun 04 05:08:07 markwilx app/web.1:     at Function.process_params (/app/node_modules/express/lib/router/index.js:335:12) 
Jun 04 05:08:07 markwilx app/web.1:     at next (/app/node_modules/express/lib/router/index.js:275:10) 
Jun 04 05:08:07 markwilx app/web.1:     at SendStream.error (/app/node_modules/serve-static/index.js:121:7) 
Jun 04 05:08:07 markwilx app/web.1:     at emitOne (events.js:116:13) 

在我努力解决此问题时,我将继续提供更新。

标签: node.jsangularexpressherokuhttp-proxy

解决方案


我还没有找到任何明确的答案,但最佳实践似乎是使用 CORS(跨源资源共享)。

在他的“保护 Angular 应用程序”一书中,Ryan Chenkie 指导读者使用代理设置用户注册路由。在第 43 页,他说:“差异可能看起来并不那么重要,但这种差异带来了天壤之别,使我们能够绕过跨域资源共享 (CORS) 等问题,也使我们能够更轻松地设置 cookie 。”

因此,我避免使用 CORS 来尝试在 Heroku 上部署我的 Angular 应用程序。然而,CORS 似乎是构建 API 的行业标准。正如 Massimiliano Sartoretto 声称的(见上文),代理似乎不适用于生产环境。我认为 CORS 不是一个可以简单地“绕过”并通过其他来源学习 CORS 的东西。

我多次尝试通过他自己的网站联系页面联系 Mr.Chenkie,以了解他的方法。他从来没有回答。因此,我有理由说我非常失望的是,他的书是关于保护 Angular 应用程序的——表面上是为了部署它们,它避免了 Web 应用程序中的这个基本安全问题。我不知道他的书中还遗漏了哪些其他关键的安全方面;因此,我不推荐他的书。去其他地方学习 Angular 应用程序网络安全。

以下是关于 CORS 的一些非常有用的解释:

请注意,预检请求(http 选项)是 Angular 应用程序学习的一个重要方面。

npm 中有一个有用的 CORS 包,您可以在这里找到: https ://www.npmjs.com/package/cors

祝你编程好运!

标记


推荐阅读