简体   繁体   中英

Spring Boot 404 on REST API calls only

Issue:

I'm working on a Spring-Boot Web Application that needs to be deployed on tomcat (WAR file). When running the application as Java (right click application.java file and run as Java in Eclipse) or running via mvnw.cmd spring-boot:run, everything works PERFECTLY (home page shows, all CRUD operations work).

When I export a WAR file and deploy to tomcat, my static web content and homepage still load fine, but any API calls return a 404 response.

This project was created using Spring Initializr. I've tried exporting via Eclipse war export, and mvnw.cmd package. Placed the WAR file in webapps directory on server and ran.

Directory structure:

在此处输入图像描述

Application file:

package com.blank.systemshare;

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

@SpringBootApplication
public class SystemshareApplication {

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

}

ServletInitializer:

package com.blank.systemshare;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer {

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

}

Controller:

package com.blank.systemshare.controller;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.blank.systemshare.exception.ResourceNotFoundException;
import com.blank.systemshare.model.System;
import com.blank.systemshare.repository.SystemRepository;

@RestController
@RequestMapping("/api/v1/")
public class SystemController {

    @Autowired
    private SystemRepository systemRepository;

    // get systems

    @GetMapping("systems")
    public List<System> getSystems() {
        return this.systemRepository.findAllByOrderByVersionDesc();
    }

    // create system
    @PostMapping("systems")
    public System createSystem(@RequestBody System system) {
        return this.systemRepository.save(system);
    }

    // update system
    @PutMapping("systems/{id}")
    public ResponseEntity<System> updateSystem(@PathVariable(value = "id") Long systemId,
                                               @Valid @RequestBody System systemDetails) throws ResourceNotFoundException {

        System system = systemRepository.findById(systemId)
                .orElseThrow(() -> new ResourceNotFoundException("System not found - ID: " + systemId));

        system.setVersion(systemDetails.getVersion());
        system.setUrl(systemDetails.getUrl());
        system.setCredentials(systemDetails.getCredentials());
        system.setFrameworks(systemDetails.getFrameworks());
        system.setFw_credentials(systemDetails.getFw_credentials());
        system.setNotes(systemDetails.getNotes());
        system.setOwner(systemDetails.getOwner());

        return ResponseEntity.ok(this.systemRepository.save(system));
    }

    //delete system
    @DeleteMapping("systems/{id}")
    public  Map<String, Boolean> deleteSystem(@PathVariable(value = "id") Long systemId) throws ResourceNotFoundException {

        System system = systemRepository.findById(systemId)
                .orElseThrow(() -> new ResourceNotFoundException("System not found - ID: " + systemId));

        this.systemRepository.delete(system);

        Map<String, Boolean> response = new HashMap<>();
        response.put("deleted",  Boolean.TRUE);

        return response;
    }

}

Repository:

package com.blank.systemshare.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.blank.systemshare.model.System;

@Repository
public interface SystemRepository extends JpaRepository<System, Long>{
    public List<System> findAllByOrderByVersionDesc();
}

App.js

var systemShareApp = angular.module('systemShareApp', [])
.controller('mainController', ($scope, $http, $window) => {

    $scope.version = '1.0';
    $scope.systemData = {};
    $scope.createSystemFormData = {};
    $scope.editSystemFormData = {};

    $scope.dbErrorStatus = false;



    // Get all systems
    var getAllSystems = function() {
        $http.get('/systemshare/api/v1/systems')
        .success((data) => {
            $scope.systemData = data;
            $scope.dbErrorStatus = false;
            console.log(data);
        })
        .error((error) => {
            console.log('Error: ' + error);
            $scope.dbErrorStatus = true;
        });
    };

  //initial load of page - get systems.
    getAllSystems();




    // Create a new system
    $scope.createSystem = () => {

        $http.post('/systemshare/api/v1/systems', $scope.createSystemFormData)
            .success((data) => {
                $scope.createSystemFormData = {};
                getAllSystems();
            })
            .error((error) => {
                console.log('Error: ' + error);
            });
    };


    // Delete a system
    $scope.deleteSystem = (systemID) => {
        $http.delete('/systemshare/api/v1/systems/' + systemID)
            .success((data) => {
                getAllSystems();
            })
            .error((data) => {
                console.log('Error: ' + data);
            });
    };

    // Update a system
    $scope.updateSystem = (systemID) => {
        $http.put('/systemshare/api/v1/systems/' + systemID, $scope.editSystemFormData)
            .success((data) => {
                $scope.editSystemFormData = {};
                getAllSystems();
            })
            .error((data) => {
                console.log('Error: ' + data);
            });
    };

    //select system function for transfer of object data in list to modal. 
    $scope.selectSystem = function(system) {

        $scope.thisSystem = system;
        //editSystemFormData Modal updates:
        $scope.editSystemFormData.owner = system.owner;
        $scope.editSystemFormData.version = system.version;
        $scope.editSystemFormData.url = system.url;
        $scope.editSystemFormData.credentials = system.credentials;
        $scope.editSystemFormData.frameworks = system.frameworks;
        $scope.editSystemFormData.fw_credentials = system.fw_credentials;
        $scope.editSystemFormData.notes = system.notes;
    };
});

Application.properties

spring.datasource.url=jdbc:postgresql://xx.xx.xxx.xxx:5432/systemshare
spring.datasource.username=xx
spring.datasource.password=xx
spring.jpa.show-sql=true

## Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen database
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect

# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto=update

server.servlet.contextPath=/systemshare

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>
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>2.2.6.RELEASE</version>
            <relativePath/> <!-- lookup parent from repository -->
        </parent>
        <groupId>com.blank</groupId>
        <artifactId>systemshare</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>war</packaging>
        <name>systemshare</name>
        <description>System Share Project</description>

        <properties>
            <java.version>1.8</java.version>
        </properties>

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

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <scope>runtime</scope>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.postgresql</groupId>
                <artifactId>postgresql</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
                <scope>provided</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>

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

mvnw.cmd spring-boot:run output (working scenario)

mvnw.cmd spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------< com.blank:systemshare >------------------------
[INFO] Building systemshare 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) > test-compile @ systemshare >>>
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ systemshare ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 4 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ systemshare ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 8 source files to C:\Users\user\Documents\JavaProjects\systemshare\target\classes
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:testResources (default-testResources) @ systemshare ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory C:\Users\user\Documents\JavaProjects\systemshare\src\test\resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:testCompile (default-testCompile) @ systemshare ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to C:\Users\user\Documents\JavaProjects\systemshare\target\test-classes
[INFO]
[INFO] <<< spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) < test-compile @ systemshare <<<
[INFO]
[INFO]
[INFO] --- spring-boot-maven-plugin:2.2.6.RELEASE:run (default-cli) @ systemshare ---
[INFO] Attaching agents: []

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-21 14:50:58.430  INFO 19748 --- [  restartedMain] c.p.systemshare.SystemshareApplication   : Starting SystemshareApplication on L37853WUS with PID 19748 (C:\Users\user\Documents\JavaProjects\systemshare\target\classes started by userin C:\Users\mck
eb2\Documents\JavaProjects\systemshare)
2020-04-21 14:50:58.435  INFO 19748 --- [  restartedMain] c.p.systemshare.SystemshareApplication   : No active profile set, falling back to default profiles: default
2020-04-21 14:50:58.513  INFO 19748 --- [  restartedMain] o.s.b.devtools.restart.ChangeableUrls    : The Class-Path manifest attribute in C:\Users\user\.m2\repository\org\glassfish\jaxb\jaxb-runtime\2.3.2\jaxb-runtime-2.3.2.jar referenced one or more files th
at do not exist: file:/C:/Users/user/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/jakarta.xml.bind-api-2.3.2.jar,file:/C:/Users/user/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/txw2-2.3.2.jar,file:/C:/Users/user/.m2/repository/org/glassfi
sh/jaxb/jaxb-runtime/2.3.2/istack-commons-runtime-3.0.8.jar,file:/C:/Users/user/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/stax-ex-1.8.1.jar,file:/C:/Users/user/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/FastInfoset-1.2.16.jar,file:/C:/U
sers/user/.m2/repository/org/glassfish/jaxb/jaxb-runtime/2.3.2/jakarta.activation-api-1.2.1.jar
2020-04-21 14:50:58.513  INFO 19748 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2020-04-21 14:50:58.513  INFO 19748 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : For additional web related logging consider setting the 'logging.level.web' property to 'DEBUG'
2020-04-21 14:50:59.303  INFO 19748 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-04-21 14:50:59.383  INFO 19748 --- [  restartedMain] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 71ms. Found 1 JPA repository interfaces.
2020-04-21 14:51:00.475  INFO 19748 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
2020-04-21 14:51:00.489  INFO 19748 --- [  restartedMain] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
2020-04-21 14:51:00.490  INFO 19748 --- [  restartedMain] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.33]
2020-04-21 14:51:00.674  INFO 19748 --- [  restartedMain] o.a.c.c.C.[.[localhost].[/systemshare]   : Initializing Spring embedded WebApplicationContext
2020-04-21 14:51:00.675  INFO 19748 --- [  restartedMain] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 2161 ms
2020-04-21 14:51:00.858  INFO 19748 --- [  restartedMain] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-04-21 14:51:00.942  INFO 19748 --- [  restartedMain] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
2020-04-21 14:51:01.117  INFO 19748 --- [  restartedMain] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-04-21 14:51:01.228  INFO 19748 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-04-21 14:51:01.603  INFO 19748 --- [  restartedMain] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-04-21 14:51:01.621  INFO 19748 --- [  restartedMain] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2020-04-21 14:51:02.803  INFO 19748 --- [  restartedMain] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-04-21 14:51:02.810  INFO 19748 --- [  restartedMain] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-04-21 14:51:02.825  INFO 19748 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2020-04-21 14:51:03.186  WARN 19748 --- [  restartedMain] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view
to disable this warning
2020-04-21 14:51:03.334  INFO 19748 --- [  restartedMain] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-21 14:51:03.402  INFO 19748 --- [  restartedMain] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2020-04-21 14:51:03.571  INFO 19748 --- [  restartedMain] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path '/systemshare'
2020-04-21 14:51:03.575  INFO 19748 --- [  restartedMain] c.p.systemshare.SystemshareApplication   : Started SystemshareApplication in 5.624 seconds (JVM running for 6.47)
2020-04-21 14:51:17.737  INFO 19748 --- [nio-8080-exec-1] o.a.c.c.C.[.[localhost].[/systemshare]   : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-04-21 14:51:17.737  INFO 19748 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2020-04-21 14:51:17.745  INFO 19748 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 7 ms

WAR deployed on tomcat (non-working scenario)

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.2.6.RELEASE)

2020-04-21 14:55:49.434  INFO 12280 --- [ost-startStop-1] com.blank.systemshare.ServletInitializer  : Starting ServletInitializer v0.0.1-SNAPSHOT on L37853WUS with PID 12280 (C:\Users\user\Documents\Software\apache-tomcat-8.5.29\webapps\systemshare\WEB-INF\classes started by user in C:\Users\user\Documents\Software\apache-tomcat-8.5.29\bin)
2020-04-21 14:55:49.451  INFO 12280 --- [ost-startStop-1] com.blank.systemshare.ServletInitializer  : No active profile set, falling back to default profiles: default
2020-04-21 14:55:52.213  INFO 12280 --- [ost-startStop-1] .s.d.r.c.RepositoryConfigurationDelegate : Bootstrapping Spring Data JPA repositories in DEFAULT mode.
2020-04-21 14:55:52.458  INFO 12280 --- [ost-startStop-1] .s.d.r.c.RepositoryConfigurationDelegate : Finished Spring Data repository scanning in 200ms. Found 1 JPA repository interfaces.
2020-04-21 14:55:53.923  INFO 12280 --- [ost-startStop-1] o.s.web.context.ContextLoader            : Root WebApplicationContext: initialization completed in 4212 ms
2020-04-21 14:55:55.125  INFO 12280 --- [ost-startStop-1] o.hibernate.jpa.internal.util.LogHelper  : HHH000204: Processing PersistenceUnitInfo [name: default]
2020-04-21 14:55:55.675  INFO 12280 --- [ost-startStop-1] org.hibernate.Version                    : HHH000412: Hibernate ORM core version 5.4.12.Final
2020-04-21 14:55:56.461  INFO 12280 --- [ost-startStop-1] o.hibernate.annotations.common.Version   : HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
2020-04-21 14:55:57.051  INFO 12280 --- [ost-startStop-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2020-04-21 14:55:57.633  INFO 12280 --- [ost-startStop-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2020-04-21 14:55:57.728  INFO 12280 --- [ost-startStop-1] org.hibernate.dialect.Dialect            : HHH000400: Using dialect: org.hibernate.dialect.PostgreSQLDialect
2020-04-21 14:56:01.538  INFO 12280 --- [ost-startStop-1] o.h.e.t.j.p.i.JtaPlatformInitiator       : HHH000490: Using JtaPlatform implementation: [org.hibernate.engine.transaction.jta.platform.internal.NoJtaPlatform]
2020-04-21 14:56:01.578  INFO 12280 --- [ost-startStop-1] j.LocalContainerEntityManagerFactoryBean : Initialized JPA EntityManagerFactory for persistence unit 'default'
2020-04-21 14:56:03.216  WARN 12280 --- [ost-startStop-1] JpaBaseConfiguration$JpaWebConfiguration : spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
2020-04-21 14:56:03.699  INFO 12280 --- [ost-startStop-1] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-04-21 14:56:04.075  INFO 12280 --- [ost-startStop-1] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page: class path resource [static/index.html]
2020-04-21 14:56:04.472  INFO 12280 --- [ost-startStop-1] com.blank.systemshare.ServletInitializer  : Started ServletInitializer in 16.741 seconds (JVM running for 32.139)
21-Apr-2020 14:56:04.517 INFO [localhost-startStop-1] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [C:\Users\user\Documents\Software\apache-tomcat-8.5.29\webapps\systemshare.war] has finished in [25,067] ms

Specific 404 error message from Chrome devtools:

{
"timestamp":"2020-04-21T19:50:27.749+0000",
"status":404,
"error":"Not Found",
"message":"No message available",
"path":"/systemshare/api/v1/systems"
}

Also, tomcat logs don't show anything useful.

Removing the ServletInitializer.java class and updating my application file to the following resolved the issue (extending SpringBootServletInitializer and overriding the configure method). Note that I tried this before, but built the application using both Eclipse and InteliJ. Both times the same issue still occurred. This final time I built using mvnw package and the WAR generated in the target directory worked.

package com.blank.systemshare;

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

@SpringBootApplication
public class SystemshareApplication extends SpringBootServletInitializer {

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

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

}

Try server.servlet.context-path instead of server.servlet.contextPath .

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