I'm trying to create a very basic Spring Boot + Angular application, which I would then be able to develop and have automatically deployed as a single application. To build the application, I configured Maven to compile the Angular project and add it to a WAR file along with my backend. Then it deploys the WAR to Heroku which launches it using the following web dyno:
java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT target/Springular-1.0.war
I'm using Webapp Runner which launches a Tomcat instance with the given WAR.
The Angular part itself is built using a postinstall script in package.json:
"postinstall": "ng build --prod --base-href /ui/"
As you can see, I've set up base-href to "/ui/" here and in pom.xml. I mapped my Spring Controller to "/api". I expected to be able to access my frontend with https://app.herokuapp.com/ui
and make backend requests like for example https://app.herokuapp.com/api/hello-world
.
While the backend part works fine, when I try to access the UI I'm being met with a 404 Whitelabel Error Page. My webapp folder (src/main/webapp) looks like this:
springular
└───src
└───main
└───webapp
│ index.jsp
└───WEB-INF
│ web.xml
web.xml doesn't contain any configuration. index.jsp sends a redirect to "/ui/". If I change the redirect to "/ui/index.html", I can access my frontend with the URL https://app.herokuapp.com/
, but when I refresh the page I get a 404 again.
Things I've tried so far:
Setting an Angular route for '' (empty) path - didn't help but I've kept it anyway.
Adding
server.servlet.context-path
to Spring properties instead of using @RequestMapping annotation.Disabling Whitelabel Error Page in Spring's application.properties, which resulted in getting a different error about not handling errors.
Specifying an error page location in web.xml. It didn't change anything.
Overriding SpringBootServletInitializer's configure method in main Java class.
Using hash location strategy in Angular (
RouterModule.forRoot(routes, { useHash: true })
)
From what I've read so far, my best guess is my Tomcat is not properly configured, but I don't know how to fix it. If it makes things easier, I'm willing to ditch the "/ui/" part from frontend URLs.
Lastly, I'll include some files I think might be relevant to the issue.
index.jsp
<% response.sendRedirect("/ui"); %>
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<display-name>Springular</display-name>
</web-app>
Main Spring Class
@SpringBootApplication
public class SpringularApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(SpringularApplication.class, args);
}
}
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>Springular</artifactId>
<version>1.0</version>
<name>springular</name>
<description>Spring + Angular Template Application</description>
<packaging>war</packaging>
<properties>
<java.version>1.8</java.version>
<heroku.appName>springular-template</heroku.appName>
<maven.compiler.source>${java.version}</maven.compiler.source>
<maven.compiler.target>${java.version}</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.4.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>2.4.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.4.1</version>
</plugin>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>${project.basedir}\frontend\dist</directory>
</fileset>
</filesets>
</configuration>
</plugin>
<plugin>
<groupId>com.github.eirslett</groupId>
<artifactId>frontend-maven-plugin</artifactId>
<version>1.11.0</version>
<configuration>
<workingDirectory>${project.basedir}\frontend</workingDirectory>
</configuration>
<executions>
<execution>
<id>install node and npm</id>
<goals>
<goal>install-node-and-npm</goal>
</goals>
<configuration>
<nodeVersion>v12.16.2</nodeVersion>
<npmVersion>6.14.4</npmVersion>
</configuration>
</execution>
<execution>
<id>npm install</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install</arguments>
</configuration>
</execution>
<execution>
<id>install angular</id>
<goals>
<goal>npm</goal>
</goals>
<configuration>
<arguments>install @angular/cli@^9.0.0 -g</arguments>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<phase>package</phase>
<goals><goal>copy</goal></goals>
<configuration>
<artifactItems>
<artifactItem>
<groupId>com.heroku</groupId>
<artifactId>webapp-runner</artifactId>
<version>9.0.41.0</version>
<destFileName>webapp-runner.jar</destFileName>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.3</version>
<configuration>
<webResources>
<resource>
<directory>frontend/dist/springular</directory>
<targetPath>ui</targetPath>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>com.heroku.sdk</groupId>
<artifactId>heroku-maven-plugin</artifactId>
<version>3.0.3</version>
<executions>
<execution>
<phase>deploy</phase>
<goals><goal>deploy-war</goal></goals>
<configuration>
<appName>${heroku.appName}</appName>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>