使用Nuxt.js和SpringBoot开始SPA/API服务器开发和部署

我整理了使用Nuxt.js v2创建SinglePageApplication(简称为SPA)和使用SpringBoot v2创建API服务器的组合进行Web应用程序开发时的环境设置以及部署到Netlify/Heroku的步骤。主要是针对Nuxt.js v2和SpringBoot v2的组合进行说明。

在建立之前

假设以下软件已经安装完毕。

    • Node.js v10

 

    • Java Development Kit(以下、JDK) 16

 

    Maven v3

另外,我们假设您已经创建了Netlify/Heroku的账户。

目录结构

假设我们采用一种单个目录结构,将Nuxt.js应用程序放置在「assets」目录中,并将Maven格式的Spring Boot应用程序放置在「server」目录中。

.
├── assets
│   ├── node_modules
│   ├── nuxt.config.js
│   ├── package.json
│   ...
│
└── server
    ├── pom.xml
    ├── src
    ...

创建开发环境

我們將使用Nuxt.js和SpringBoot創建一個可以進行熱重載的環境。

                                                 +-------------+
               +------------+      /api          |             |
               |            +------------------->| Spring Boot |
               |            |   localhost:8080   |             |
               | Nuxt.js    |                    +-------------+
-------------->| Dev Server |
localhost:3000 |            |
               |            |
               +------------+

设置只允许从Web应用程序使用者访问localhost:3000,这样就不需要处理跨域相关的事项了。这个配置的要点有两点。

    • /api へのリクエストは Nuxt.js Dev Server が localhost:8080 で動作している SpringBoot に転送(Proxy)し、SpringBoot が処理する。

 

    その他の URL へのリクエストは、静的リソースへのアクセスとして Nuxt.js 開発サーバが処理する。

创建 Nuxt.js 应用

请安装Nuxt应用程序生成器create-nuxt-app。

$ npm i -g create-nuxt-app

由于我想将Nuxt.js应用程序的源代码放置在assets文件夹下,所以我将应用程序命名为assets并创建Nuxt应用程序。您将被询问要使用的框架,您可以根据喜好进行选择。在”Choose rendering mode”中选择”Single Page App”,在”Use axios module”中选择”yes”。

$ create-nuxt-app assets
> Generating Nuxt.js project ...
? Project name [assets]
? Project description [My sublime Nuxt.js project]
? Use a custom server framework [none]
? Use a custom UI framework [none]
? Choose rendering mode [Single Page App]
? Use axios module [yes]
? Use eslint [yes]
? Use prettier [yes]
? Author name []
? Choose a package manager [npm]

如果在「选择渲染模式」中选择「Universal」,则可以进行服务器端渲染。由于在服务器端进行渲染,会给服务器增加负担,因此在实现服务器端渲染时需要注意一些事项,不适合快速开发。而「Single Page App」是仅在浏览器端进行渲染的模式,对服务器友好,且在应用开发中的限制较少,因此选择这个模式。

「使用axios模块」是一个可以简化异步的HTTP(S)通信的模块。它是与API服务器通信所必需的。

Nuxt.js 应用的配置

为了将对 localhost:3000/api 的访问转发到 localhost:8080/api(SpringBoot),请在Nuxt.js应用程序中添加代理模块。请进入 “assets” 文件夹并添加 “@nuxtjs/proxy” 模块。

$ npm i --save @nuxtjs/proxy

在 Nuxt.js 中,assets/nuxt.config.js 是配置文件。为了识别 Proxy 模块,请在此文件的 “modules” 部分添加 “@nuxtjs/proxy”。

   modules: [
     // Doc: https://github.com/nuxt-community/axios-module#usage
-    '@nuxtjs/axios'
+    '@nuxtjs/axios',
+    '@nuxtjs/proxy'
   ],

进行axios模块的配置。”credentials”选项是用于指示在进行HTTP通信时是否传递cookie等敏感信息的选项。本文不涉及此部分,但在创建仅能在已登录情况下访问的API时会用到。”proxy”选项在与proxy模块一起使用时需要进行配置。

   axios: {
     // See https://github.com/nuxt-community/axios-module#options
+    credentials: true,
+    proxy: true
   },

接下来将进行 axios 模块的设置。

将对 localhost:3000/api 的访问转发至 localhost:8080/api (SpringBoot)。

有一个故事,我们在这里进行设置。另外,在代理下运行Spring Boot时,需要通过“X-Forwarded-Host”头部传递原始主机名。下面将解释为什么需要这样做。

+
+  proxy: {
+    '/api/': {
+      target: 'http://localhost:8080',
+      headers: { 'X-Forwarded-Host': 'localhost:3000' }
+    }
   },

为什么需要 X-Forwarded-Host 头部

在使用Spring Boot运行在代理服务器下时,需要通过”X-Forwarded-Host”头信息来告知原始主机名的原因。

HTTP 301, 303在Location头部执行重定向,但根据HTTP规范,可以指定在Location头部的URL应是绝对路径。在RFC2616日本语翻译14.30 Location中有详细说明。据认为,Spring Boot(基于Spring Framework)也符合这一规范。因此,使用Spring Boot进行重定向处理时,会将重定向定向到以http://localhost:8080/开头的URL。

因此,可以通过在Spring Boot中使用”X-Forwarded-Host”头来通知原始主机名,而不是自己正在运行的主机名,以便构建基于转发源主机名的重定向URL,并将操作重定向到以”http://localhost:3000/”开头的URL。实际上,在构建URL的org.springframework.web.util.UriComponentsBuilder类中,如果有”X-Forwarded-Host”头的指定,就会有使用指定的主机名来构建URL的处理过程。实际的源代码

创建Spring Boot应用程序

我将以Maven项目的形式编写Spring Boot应用程序。像往常一样,我们将使用”spring-boot-starter-parent”作为父项目。在这里,我们决定创建一个简单的API服务器,它仅返回请求时间的接口:”/api/time”。为了创建一个RESTful的WebAPI,我们将在Maven的dependencies中添加”Spring MVC”和”Developer Tools”,以便实现热重载。在JDK9中,对内部API的访问进行了限制的行为更改。由于在JDK9及更高版本的JDK上运行旧的SpringBoot版本会导致错误发生,请尽量使用更新的SpringBoot版本。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.example</groupId>
  <artifactId>com.example.server</artifactId>
  <version>1.0</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
  </properties>

  <parent>
    <!-- Spring Core -->
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.5.4</version>
  </parent>

  <dependencies>
    <!-- Spring MVC -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Developer Tools -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-devtools</artifactId>
    </dependency>
  </dependencies>
</project>

由于Spring Boot在创建RESTful WebAPI方面有很多详细说明,因此我在此省略了具体讲解。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

  public static void main(String[] arguments) {
    SpringApplication.run(Application.class, arguments);
  }
}
package com.example.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@RestController
public class TimeController {

  @RequestMapping("/api/time")
  public Map<String, String> index() {
    Map<String, String> response = new HashMap<>();
    response.put("date", new Date().toString());
    return response;
  }
}

如何开始开发

启动 Nuxt.js 开发服务器。

$ npm run dev

用另一个终端启动Spring Boot。

$ mvn spring-boot:run

目前两个都以启用热重载的状态启动。为了确认热重载是否可行,我们将尝试在主页上显示从 “/api/time” 获取的日期和时间。保存并按以下方式重写后,Nuxt.js 的热重载功能将立即重新加载并反映这些更改。

<template>
  <h1>{{ date }}</h1>
</template>

<script>
module.exports = {
  data() {
    return {
      date: ''
    }
  },
  async asyncData({ app }) {
    const response = await app.$axios.get('/api/time')
    console.log(response.data.date)
    return { date: response.data.date }
  }
}
</script>

<style>
</style>

部署

本文介绍了将SPA部署到Netlify以及将Spring Boot应用部署到Heroku的方法。

                                                 +-----------------+
               +------------+      /api          |                 |
               |            +------------------->|   Spring Boot   |
               |            |                    | (Heroku Server) |
               |  Netlify   |                    |                 |
-------------->| Web Server |                    +-----------------+
               |            |
               |            |
               +------------+

将开发环境配置与原始一致。

    • Netlify の Web サーバの /api へのリクエストは Heroku サーバで動作する SpringBoot の /api に転送して、SpringBoot が処理する。

 

    その他の URL へのリクエストは、静的リソースへのアクセスとして Netlify の Web サーバが処理する。

将代码部署到Netlify。

Netlify是一个提供静态网站托管服务的平台。它不仅可以简单地进行托管,还可以与git存储库进行集成,实现自动构建等功能。此外,还可以通过netlify.toml配置文件进行自定义。虽然无法进行服务器端渲染(SSR),但作为SPA的部署目标,其具备足够的功能。

以下将解释在 SPA/API 服务器配置情况下,通过 netlify.toml 文件进行设置的方法。需要注意的有两点。

    • API サーバの設定では、転送対象のパスと転送先だけでなく、「X-Forwarded-Host」ヘッダでオリジナルサーバのホスト名も送るように設定します。

 

    「/」以外の URL へ直接アクセスした場合はトップページと同じ内容を返すように設定します。この設定がないと「/」以外の URL に直接アクセスすると 404 NotFound です。
# APIサーバの設定
[[redirects]]
  from = "/api/*"
  to = "https://<あなたのherokuホスト名>/api/:splat"
  status = 200
  [redirects.headers]
    X-Forwarded-Host = "<あなたのnetlifyホスト名>"

# SPAの設定
[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

以下是向 Netlify 部署的步骤。请确保已经安装了 Netlify 命令行工具。

$ npm install -g netlify-cli

此外,我会打开一个新的终端,并确保已登录到Netlify。

$ netlify login

执行构建命令在「assets」文件夹中,会在「assets/dist」文件夹中生成 SPA 应用程序。

$ npm run build

使用Netlify命令将“dist”目录部署。如果是第一次部署,可能会询问您一些问题,请根据您的喜好回答。

$ netlify deploy

将应用程序部署到Heroku

Heroku 是一个能够托管 Java、Ruby、Node.js 服务器应用的服务。
有几种方法可以将应用部署到Heroku,常见的是将Heroku应用的Git存储库推送到部署。但是,本次我们将使用Maven来进行部署。由于该项目将Maven项目配置在顶层目录下的子目录中,因此部署会变得困难。

Maven 通过使用「heroku-maven-plugin」来部署到Heroku平台。

    • jdkVersion タグの中にはお使いの JDK バージョン

 

    web タグの中には JavaVM オプション

如果您有使用Heroku的经验,可能曾经编写过Procfile文件。但是,如果您想使用”heroku-maven-plugin”,则无需创建此文件。

   </dependencies>
+  <build>
+    <plugins>
+      <!-- Heroku deploy settings -->
+      <plugin>
+        <groupId>com.heroku.sdk</groupId>
+        <artifactId>heroku-maven-plugin</artifactId>
+        <version>2.0.6</version>
+        <configuration>
+          <jdkVersion>1.8</jdkVersion>
+          <processTypes>
+            <web>java -Duser.language=ja -Duser.country=JP -Duser.timezone=Asia/Tokyo -jar ./target/com.example.server-1.0.jar</web>
+          </processTypes>
+        </configuration>
+      </plugin>
+    </plugins>
+  </build>
 </project>
    「-jar ./target/com.example.server-1.0.jar」

使用Maven进行构建时,将生成包含Spring Boot本身和您编写的应用程序的JAR文件,该文件位于“target/com.example.server-1.0.jar”。这意味着执行该文件。

    「-Duser.language=ja -Duser.country=JP -Duser.timezone=Asia/Tokyo」

这是将语言、时区等(即所谓的本地区)设置为日本的选项。

请设置环境变量“HEROKU_API_KEY”,然后进行构建和部署。可在https://dashboard.heroku.com/account页面找到环境变量“HEROKU_API_KEY”的相关信息。

$ HEROKU_API_KEY=<APIキー> mvn clean install heroku:deploy -Dheroku.appName=<herokuアプリ名>

结束时

Nuxt.js和Spring Boot都有很多解說網站和書籍,可以輕鬆開始使用。
通過使用Netlify和Heroku來執行,現在可以輕鬆地將Web服務公開。
在這裡所介紹的內容都可以在免費範圍內完成,所以大家也一定要試試看。

bannerAds