当我尝试在一个有很多移动部件的应用程序中从 springdoc 检索 api-docs 时,我遇到了错误 404。为了调试问题,我构建了一个简单的 springboot 应用程序,其中只有用于测试 springdoc 的基本代码。
我按照 https://springdoc.org/index.html#getting-started 中的说明进行操作,在尝试检索 api-docs 端点时收到错误 404。
我想我一路上错过了一些东西,但看不到它是什么。
pom.xml 是:
<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>org.ihc.hdd.s3</groupId>
<artifactId>springboot-example</artifactId>
<version>1.1.0-SNAPSHOT</version>
<name>springboot-example</name>
<description>Example Spring Boot S3 Project</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</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>21</java.version>
</properties>
<dependencies>
<!-- project dependencies -->
<!-- Spring boot starters -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>2.7.0</version>
</dependency>
<!-- Test Dependencies -->
<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-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<finalName>${project.artifactId}</finalName>
</build>
</project>
控制器是:
/**
*
*/
package org.ihc.hdd.s3.controller;
import org.ihc.hdd.s3.models.HelloWorldApi;
import org.ihc.hdd.s3.service.HelloWorldService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
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.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import io.swagger.v3.oas.annotations.Operation;
/**
* @author dtsteven
*
* The controller should just contain logic to read from and write to
* the REST interface. All other work should be done by the service.
*/
@RestController
@RequestMapping("/hello")
public class HelloWorldController {
@Autowired
private HelloWorldService helloWorldService;
@PostMapping("/world")
@Operation(summary = "Post hello world ")
public ResponseEntity<?> newHelloWorld(@RequestBody HelloWorldApi hello) {
if (hello.getPosition() != null) {
return ResponseEntity.badRequest().body("Position is not valid on POST record");
}
HelloWorldApi result = this.helloWorldService.saveHelloWorldRecord(hello);
return ResponseEntity.status(HttpStatus.CREATED).body(result);
}
@GetMapping("/world/{id}")
@Operation(summary = "Get hello world record that was created. ", description = "{id} Must be less than records created with post")
public ResponseEntity<?> getHelloWorldSingle(@PathVariable Integer id) {
HelloWorldApi result = this.helloWorldService.getHelloWorldRecord(id);
if (result == null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(result);
}
@GetMapping("/world")
@Operation(summary = "Get hello world ")
public ResponseEntity<String> getHelloWorld() {
return ResponseEntity.ok("Hello World");
}
}
application.yml:
server:
context-path: /springboot-example
spring:
profiles:
active: local
# Spring Actuator
endpoints:
enabled: false # Disable all endpoints
info:
enabled: true # Enable only the info endpoint
health:
enabled: true
# This information will be displayed at /monitor/info using spring actuator
info:
app:
name: '@project.name@' # @{value}@ are pulled from the pom.xml
description: '@project.description@'
version: '@project.version@'
artifactId: '@project.artifactId@'
application-local.yml:
server:
port: 8888
logging:
level:
org.springframework.web: TRACE
当我启动应用程序时,我包含 TRACE 消息并获得以下结果。 请注意,它会查找控制器中定义的端点、api-docs 和 swagger-ui 的端点。 我可以看到所有端点都已定义。
2024-12-12T14:43:30.447-07:00 INFO 39932 --- [ main] o.i.hdd.s3.SpringbootExampleServicesApp : Starting SpringbootExampleServicesApp using Java 21.0.3 with PID 39932 (C:\Users\dtsteven\dev\git\samples\springboot-example\target\classes started by dtsteven in C:\Users\dtsteven\dev\git\samples\springboot-example)
2024-12-12T14:43:30.452-07:00 INFO 39932 --- [ main] o.i.hdd.s3.SpringbootExampleServicesApp : The following 1 profile is active: "local"
2024-12-12T14:43:32.628-07:00 INFO 39932 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8888 (http)
2024-12-12T14:43:32.649-07:00 INFO 39932 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2024-12-12T14:43:32.649-07:00 INFO 39932 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.33]
2024-12-12T14:43:32.804-07:00 INFO 39932 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2024-12-12T14:43:32.805-07:00 INFO 39932 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2270 ms
2024-12-12T14:43:33.010-07:00 DEBUG 39932 --- [ main] o.s.w.f.ServerHttpObservationFilter : Filter 'webMvcObservationFilter' configured for use
2024-12-12T14:43:33.277-07:00 TRACE 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.i.h.s.c.HelloWorldController:
{GET [/hello/world/{id}]}: getHelloWorldSingle(Integer)
{POST [/hello/world]}: newHelloWorld(HelloWorldApi)
{GET [/hello/world]}: getHelloWorld()
2024-12-12T14:43:33.293-07:00 TRACE 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.s.b.a.w.s.e.BasicErrorController:
{ [/error]}: error(HttpServletRequest)
{ [/error], produces [text/html]}: errorHtml(HttpServletRequest,HttpServletResponse)
2024-12-12T14:43:33.313-07:00 TRACE 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.s.w.a.OpenApiWebMvcResource:
{GET [/v3/api-docs], produces [application/json]}: openapiJson(HttpServletRequest,String,Locale)
{GET [/v3/api-docs.yaml], produces [application/vnd.oai.openapi]}: openapiYaml(HttpServletRequest,String,Locale)
2024-12-12T14:43:33.318-07:00 TRACE 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.s.w.u.SwaggerWelcomeWebMvc:
{GET [/swagger-ui.html]}: redirectToUi(HttpServletRequest)
2024-12-12T14:43:33.320-07:00 TRACE 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping :
o.s.w.u.SwaggerConfigResource:
{GET [/v3/api-docs/swagger-config], produces [application/json]}: openapiJson(HttpServletRequest)
2024-12-12T14:43:33.324-07:00 DEBUG 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : 9 mappings in 'requestMappingHandlerMapping'
2024-12-12T14:43:33.330-07:00 DEBUG 39932 --- [ main] o.s.w.s.h.BeanNameUrlHandlerMapping : Detected 0 mappings in 'beanNameHandlerMapping'
2024-12-12T14:43:33.400-07:00 TRACE 39932 --- [ main] o.s.w.s.f.support.RouterFunctionMapping : 0 RouterFunction(s) in 'routerFunctionMapping'
2024-12-12T14:43:33.427-07:00 TRACE 39932 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped [/webjars/**] onto ResourceHttpRequestHandler [classpath [META-INF/resources/webjars/]]
2024-12-12T14:43:33.427-07:00 TRACE 39932 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped [/**] onto ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]
2024-12-12T14:43:33.428-07:00 TRACE 39932 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped [/swagger-ui*/*swagger-initializer.js] onto ResourceHttpRequestHandler [classpath [META-INF/resources/webjars/]]
2024-12-12T14:43:33.429-07:00 TRACE 39932 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped [/swagger-ui*/**] onto ResourceHttpRequestHandler [classpath [META-INF/resources/webjars/]]
2024-12-12T14:43:33.429-07:00 DEBUG 39932 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Patterns [/webjars/**, /**, /swagger-ui*/*swagger-initializer.js, /swagger-ui*/**] in 'resourceHandlerMapping'
2024-12-12T14:43:33.465-07:00 DEBUG 39932 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : ControllerAdvice beans: 0 @ModelAttribute, 0 @InitBinder, 1 RequestBodyAdvice, 1 ResponseBodyAdvice
2024-12-12T14:43:33.542-07:00 DEBUG 39932 --- [ main] .m.m.a.ExceptionHandlerExceptionResolver : ControllerAdvice beans: 1 @ExceptionHandler, 1 ResponseBodyAdvice
2024-12-12T14:43:33.976-07:00 INFO 39932 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 1 endpoint beneath base path '/actuator'
2024-12-12T14:43:34.078-07:00 INFO 39932 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8888 (http) with context path '/'
2024-12-12T14:43:34.102-07:00 INFO 39932 --- [ main] o.i.hdd.s3.SpringbootExampleServicesApp : Started SpringbootExampleServicesApp in 4.444 seconds (process running for 6.373)
2024-12-12T14:43:34.626-07:00 INFO 39932 --- [on(4)-127.0.0.1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2024-12-12T14:43:34.626-07:00 INFO 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2024-12-12T14:43:34.627-07:00 TRACE 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.multipart.support.StandardServletMultipartResolver@4a8dbea6
2024-12-12T14:43:34.627-07:00 TRACE 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver@26276164
2024-12-12T14:43:34.627-07:00 TRACE 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected org.springframework.web.servlet.theme.FixedThemeResolver@26278361
2024-12-12T14:43:34.628-07:00 TRACE 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected DefaultRequestToViewNameTranslator
2024-12-12T14:43:34.629-07:00 TRACE 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Detected SessionFlashMapManager
2024-12-12T14:43:34.631-07:00 DEBUG 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : enableLoggingRequestDetails='false': request parameters and headers will be masked to prevent unsafe logging of potentially sensitive data
2024-12-12T14:43:34.631-07:00 INFO 39932 --- [on(4)-127.0.0.1] o.s.web.servlet.DispatcherServlet : Completed initialization in 5 ms
当我在浏览器中输入此 URL 时,收到 404 错误。
http://localhost:8888/springboot-example/v3/api-docs
这是生成的日志:
2024-12-12T14:44:07.459-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : GET "/springboot-example/v3/api-docs", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2024-12-12T14:44:07.467-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped to HandlerExecutionChain with [ResourceHttpRequestHandler [classpath [META-INF/resources/], classpath [resources/], classpath [static/], classpath [public/], ServletContext [/]]] and 3 interceptors
2024-12-12T14:44:07.481-07:00 DEBUG 39932 --- [nio-8888-exec-1] o.s.w.s.r.ResourceHttpRequestHandler : Resource not found
2024-12-12T14:44:07.486-07:00 DEBUG 39932 --- [nio-8888-exec-1] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.servlet.resource.NoResourceFoundException: No static resource springboot-example/v3/api-docs.]
2024-12-12T14:44:07.486-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : No view rendering, null ModelAndView returned.
2024-12-12T14:44:07.486-07:00 DEBUG 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Completed 404 NOT_FOUND, headers={masked}
2024-12-12T14:44:07.493-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : "ERROR" dispatch for GET "/error", parameters={}, headers={masked} in DispatcherServlet 'dispatcherServlet'
2024-12-12T14:44:07.495-07:00 TRACE 39932 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : 2 matching mappings: [{ [/error], produces [text/html]}, { [/error]}]
2024-12-12T14:44:07.496-07:00 TRACE 39932 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped to org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController#errorHtml(HttpServletRequest, HttpServletResponse)
2024-12-12T14:44:07.507-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.web.method.HandlerMethod : Arguments: [org.apache.catalina.core.ApplicationHttpRequest@42d5a1e6, org.springframework.web.context.request.async.StandardServletAsyncWebRequest$LifecycleHttpServletResponse@360f1ef1]
2024-12-12T14:44:07.524-07:00 TRACE 39932 --- [nio-8888-exec-1] s.w.s.m.m.a.RequestMappingHandlerAdapter : Applying default cacheSeconds=-1
2024-12-12T14:44:07.528-07:00 DEBUG 39932 --- [nio-8888-exec-1] o.s.w.s.v.ContentNegotiatingViewResolver : Selected 'text/html' given [text/html, text/html;q=0.8]
2024-12-12T14:44:07.528-07:00 TRACE 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Rendering view [org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration$StaticView@4050f757]
2024-12-12T14:44:07.534-07:00 DEBUG 39932 --- [nio-8888-exec-1] o.s.web.servlet.DispatcherServlet : Exiting from "ERROR" dispatch, status 404, headers={masked}
没有定义配置。 文档表明这应该开箱即用。
我很感激任何帮助。 谢谢。
我解决了这个问题。 感谢 roar-s 建议查看已经运行的示例代码。 我无法找到一些正确运行代码的示例。 最终我添加了上下文路径,但没有使用正确的属性路径。 它应该是 server.servlet.context-path。