使用前端应用程序部署 akka-http 应用程序

Deployment of akka-http app with frontend app

我想弄清楚如何使用适当的前端应用程序部署 akka-http。

让我们假设我们有提供一些 API 的 akka-http 应用程序。它位于回购协议 A 中。 对于这个服务器端应用程序,我们有前端应用程序(Angular 或 REACT 或其他)。它位于回购 B.

那么应该如何正确部署在一起呢?

我正在概述以下场景:

  1. 检查 A 存储库
  2. 导航至 /src/main/public 并签出 B 存储库
  3. 借助 jar 中的 SBT 构建 akka-http 存储库
  4. 在专用服务器上部署 jar

场景不好吗?

这可能不是您想要的,但我有一个种子项目,其中包含 Akka Http 和 Angular 5 个合一的存储库(您要求 2 个存储库)配置为部署到 Heroku单测功机。

https://github.com/jdschmitt/akka-angular-heroku

希望对您有所帮助。

最好的方法可能是单独部署前端应用程序。但将后端和前端应用程序部署为单个捆绑包也是一种常见的做法(例如 JHipster 做法)。不过,我可以回答如何捆绑。

为方便起见,您可以在单个 SBT 项目中创建两个模块 - serviceui(前端应用程序的根目录)。一个用于后端,另一个用于前端。

SBT 设置

根据您使用的前端框架,我们将稍微修改 SBT 设置。假设我们使用 React。默认情况下,如果我们 运行 npm buildyarn build,前端捆绑文件将默认位于 ui/build 目录中。我们将在 ui 模块中将 build 目录标记为“资源”:

lazy val `ui` =
  project
    .in(file("./ui"))
    .settings(
      resourceGenerators in Compile += buildUi.init
    )

lazy val buildUi = taskKey[Seq[File]]("Generate UI resources") := {
  val webapp = baseDirectory.value / "build"
  val managed = resourceManaged.value
  for {
    (from, to) <- webapp ** "*" pair Path.rebase(webapp, managed / "main" / "ui")
  } yield {
    Sync.copy(from, to)
    to
  }
}

service 模块将依赖于 ui 模块:

lazy val `service` =
  project
    .in(file("./service"))
    .dependsOn(`ui`)

现在,service 可以在您构建 React 应用程序后从 ui 中获取资源文件。

如何与后端一起提供前端应用程序 API

假设您创建了 API 路由,前端将使用这些路由。创建一个以“api”、“v1”、“api/v1”或其他任何开头的 pathPrefix 路由,稍后你会看到为什么我们需要这个前缀:

pathPrefix("api") { // api routes }

并创建另一个为前端资产提供服务的路由:

def assets: Route =
      getFromResourceDirectory("ui") ~ 
      pathPrefix("") {
        get {
          getFromResource("ui/index.html", ContentType(`text/html`, `UTF-8`))
        }
      }

然后这样加入两条路线:

pathPrefix("api") seal { // api routes } ~ assets

瞧!

让我们解释一下这些路线。

首先,我们要匹配我们的 API 路线,因为它们位于特定的 URL。 assets 匹配所有其他 URL。这意味着,访问任何不以 /api 开头的 URL 将 return React 的静态资源。

接下来,让我们剖析assets条路线:

第一个是 getFromResourceDirectory("ui")。还记得我们将 ui/build 目录标记为资源目录吗?这意味着我们的 React 资源位于 target/scala/classes/ui 目录中,我们可以简单地以这种方式提供它们。

第二个也是最棘手的一个:

pathPrefix("") {
  get {
    getFromResource("ui/index.html", ContentType(`text/html`, `UTF-8`))
  }
}

这意味着任何不以 /api 开头的 URL 将匹配此路由和 return React 的 index.html 文件。但您可能会问:“为什么我们不直接使用 pathSingleSlash?”这正是棘手的部分 - 前端框架路由。

假设我们使用 pathSingleSlash(我们仅在根“/”上公开 React 的资源)。假设我们使用例如react-router-dom 用于 React 应用程序中的路由。我们有一个很好的“/users”路由,可以在 table 中显示用户。我们转到“/”-> Akka HTTP 服务器提供静态文件,一切都按预期工作->我们单击一些转到“/users”的按钮,一切都按预期再次工作,因为 React 路由器现在正在执行路由-> 然后我们刷新我们的页面,我们得到一个 404 错误。为什么?因为它在 React 应用程序中是已知路由,但在 Akka HTTP 服务器中是未知路由。因此,在除 API 路由 ("/api") 之外的任何路由中,我们都希望公开我们的 React 资源。使用 pathPrefix(""),我们正是这样做的。当我们转到“/users”页面并点击刷新时,Akka HTTP 仍然 returns React 的资源,其余的路由魔术由 React 完成 - “/users”页面成功呈现。

您还可以创建 CORS 路由,这样您就可以在开发阶段从后端独立 运行 前端。

哈金快乐。