简体   繁体   中英

Deploying Spring Boot WAR to Tomcat 8 - HTTP 404 when accessing resources

I'm new to Spring Boot and struggling to deploy a simple HTML web app (AngularJS) to Tomcat 8. This web app simply serves some HTML/CSS/JS content with no REST calls to a backend. It has been "compiled" using Webpack -- this produces JS/CSS bundles and a single index.html file that points to them via <script> / <link> tags -- and has been tested on ExpressJS and Spring Boot w/ Embedded tomcat and works as expected. But going down the path of a stand-alone WAR and deploying to Tomcat 8 does not seem to work properly.

For the Spring Boot project, I've included all the HTML/CSS/JS files in the src/main/resources/public folder (no subfolders) and have also configured the pom.xml as follows (basic config):

<?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.my-app</groupId>
    <artifactId>my-app</artifactId>
    <version>0.0.1</version>
    <packaging>war</packaging>

    <name>my-app</name>
    <description>Demo project for Spring Boot</description>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

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

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-tomcat</artifactId>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

"Main" class:

package com.example.my.app;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;

@SpringBootApplication
public class MyAppApplication extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(MyAppApplication.class);
    }

    public static void main(String[] args) {
        SpringApplication.run(MyAppApplication.class, args);
    }
}

Here are the steps I used to do the Tomcat deployment:

  1. mvn clean package to generate the WAR
  2. Copy the WAR to path/to/tomcat8/webapp
  3. Start Tomcat
  4. http://localhost:8080/my-app => auto-loads index.html

Unfortunately all I see are 404 errors because it couldn't find some JS/CSS file. Is there a config I'm missing?

Updated : Here is the index.html file (auto-generated via Webpack):

<!doctype html>
<html>
  <head>
    <base href="/">
    <meta charset="utf-8">
    <title>My App</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width">
    <link rel="icon" type="image/png" href="./assets/images/favicon.ico" />
  <link href="/main-aa49893f83cc830596563d81f09a9611.css" rel="stylesheet"><link href="/main-5949f1f257a55a77e48bc4ab62fbc99a.css" rel="stylesheet"></head>

  <body ng-app="myapp">
    <ui-view></ui-view>
  <script type="text/javascript" src="vendor-353ddb48f4ffe6546d59.js"></script><script type="text/javascript" src="app-353ddb48f4ffe6546d59.js"></script></body>
</html>

Here are the errors I'm seeing in Chrome Web Inspector when visiting localhost:8080/my-app/index.html :

http://localhost:8080/main-5949f1f257a55a77e48bc4ab62fbc99a.css Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/main-aa49893f83cc830596563d81f09a9611.css Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/vendor-4ba9083ed9802279c207.js Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/app-4ba9083ed9802279c207.js Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/vendor-4ba9083ed9802279c207.js Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/app-4ba9083ed9802279c207.js Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/main-aa49893f83cc830596563d81f09a9611.css Failed to load resource: the server responded with a status of 404 (Not Found)
http://localhost:8080/main-5949f1f257a55a77e48bc4ab62fbc99a.css Failed to load resource: the server responded with a status of 404 (Not Found)

One more thing I forgot to mention. When I generate the war using mvn clean package , all the files that are under src/main/resources/public are placed into the WEB-INF/classes/resource subfolder. According to research, these files are not publicly visible (ie if I try to access localhost:8080/my-app/foo.css, it'll give 404). Is this why the index.html file is unable to "see" the JS/CSS files it depends on?

The most common issue with the setup you have described, without more context, is that you are most likely using absolute URLs to point at your js / css files. As such when you put them into the servlet container under /my-app they no longer reference properly as they are trying to go to the root / . You need to use relative URLs when describing the location of the resource files on the path.

I ended up figuring it out but not using Spring Boot to package the WAR file. There was another project lying around on my local that used plain old Maven + pom.xml + web.xml to create WARs, which I used as a reference to figure out why the current project was not working. There were multiple issues:

  • When you deploy onto Tomcat 8 using default config, it will append the name of the WAR file (what they refer to as Context) to its path. In this case, it was http://localhost:8080/my-app . The "compiled" AngularJS app's index.html had a <base href="/"> tag that needed to point to /my-app/ instead of / . This was the main reason why the JS/CSS files were not visible in Web Inspector > Sources .
  • <link> tag's src attribute was not supposed to contain a leading /
  • In the case of the Spring Boot App I posted above, it comes with an Embedded Tomcat and deploys the app at the Root Context so there's no need to change any paths in the index.html file.
  • Similar to Spring Boot, I also had no issues running the app in ExpressJS since a "sub-context" was not created. Again, there was no need to modify any files in this case.

There were other errors related to finding resources like .ttf files but at least the app was able to run.

Update: Looks like its possible to serve the WAR file from the Tomcat 8 root by adding the following near the bottom of the server.xml file:

<Context path="" docBase="my-app" debug="0" reloadable="true"></Context>

This will prevent the need to modify the index.html file.

Awesome :)

I had the same problem, and it is related to Webpack more than to Spring-boot. In your webpack.common.js / webpack.prod.js you have to set :

metada.baseUrl = './';

and inject this in your <base> link in index.html

And you also have to set

output.publicPath: './'

With both variable set, this worked for me.

Uhmm i've tried some of this on my spring-boot project.

First i'd add a dependency on my project pom.xml

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

then i'd add an index.html on src/main/resources/templates

after that you should add a controller to point to that index

@Controller
public class AppController {

    @RequestMapping("/")
    String index() {
        return "index";
    }
}

For more information see this guide. Spring-boot and Thymeleaf

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM