仅在 REST API 调用上的 Spring Boot 404
Posted
技术标签:
【中文标题】仅在 REST API 调用上的 Spring Boot 404【英文标题】:Spring Boot 404 on REST API calls only 【发布时间】:2020-08-04 16:54:22 【问题描述】:问题:
我正在开发一个需要部署在 tomcat(WAR 文件)上的 Spring-Boot Web 应用程序。当以 Java 运行应用程序(右键单击 application.java 文件并在 Eclipse 中以 Java 运行)或通过 mvnw.cmd spring-boot:run 运行时,一切正常(主页显示,所有 CRUD 操作都有效)。
当我导出 WAR 文件并部署到 tomcat 时,我的静态 Web 内容和主页仍然可以正常加载,但任何 API 调用都会返回 404 响应。
这个项目是使用 Spring Initializr 创建的。我试过通过 Eclipse 战争导出和 mvnw.cmd 包导出。将 WAR 文件放在服务器上的 webapps 目录中并运行。
目录结构:
申请文件:
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);
控制器:
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;
存储库:
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 输出(工作场景)
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
在 tomcat 上部署 WAR(非工作场景)
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: 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
来自 Chrome devtools 的特定 404 错误消息:
"timestamp":"2020-04-21T19:50:27.749+0000",
"status":404,
"error":"Not Found",
"message":"No message available",
"path":"/systemshare/api/v1/systems"
另外,tomcat 日志没有显示任何有用的信息。
【问题讨论】:
使用@GetMapping("/systems")
是否有帮助,使用前导斜杠
不,这最终不是问题,因为应用程序在直接从 spring-boot run 命令运行时工作正常。我确实尝试过操纵映射以进行故障排除。我在下面的回答为我解决了问题。
【参考方案1】:
删除 ServletInitializer.java 类并将我的应用程序文件更新为以下解决了该问题(扩展 SpringBootServletInitializer 并覆盖配置方法)。请注意,我之前尝试过,但是使用 Eclipse 和 InteliJ 构建了应用程序。两次仍然出现相同的问题。这最后一次我使用mvnw package
构建,并且在目标目录中生成的 WAR 有效。
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);
【讨论】:
以上是关于仅在 REST API 调用上的 Spring Boot 404的主要内容,如果未能解决你的问题,请参考以下文章
如何仅在 Django Rest Framework 中的另一个 API 方法中调用 API POST 方法
用于非授权连接的 Spring Security REST Api
在spring boot应用程序中从自己的rest api调用另一个rest api