【问题标题】:Java SpringBoot OpenApi @ApiResponse shows wrong return objectJava SpringBoot OpenApi @ApiResponse 显示错误的返回对象
【发布时间】:2021-12-23 20:44:36
【问题描述】:

我在我的 SpringBoot 项目中使用 OpenApi 3 来生成一个 Swagger html 页面。

POM.xml 中的依赖:

    <dependency>
        <groupId>org.springdoc</groupId>
        <artifactId>springdoc-openapi-ui</artifactId>
        <version>1.5.12</version>
    </dependency>

在 Controller 类中,我在方法上方定义了以下注解。

@Operation(
        summary = "Get a list of letters for a specific user",
        description = "Get a list of letters for a specific user",
        tags = {"letters"}
)
@ApiResponses(value = {
        @ApiResponse(responseCode = "200", description = "success", content = {@Content(
                                                                    mediaType = "application/json",
                                                                    array = @ArraySchema(schema = @Schema(implementation = LetterDTO.class)))}),
        @ApiResponse(responseCode = "400", description = "BAD REQUEST"),
        @ApiResponse(responseCode = "401", description = "UNAUTHORIZED"),
        @ApiResponse(responseCode = "403", description = "Forbidden"),
        @ApiResponse(responseCode = "404", description = "NOT_FOUND: Entity could not be found")}
)
@GetMapping(value = "letters/user/{userId}", produces = {"application/json"})
public List<LetterDTO> getLettersForUser(
    ...
)

Swagger UI 的输出显示代码 200 的正确响应,它是 LetterDTO 对象的列表。

但代码 401 的响应还显示了一个 LetterDTO 对象列表。很难,我没有为代码 401 定义任何响应对象。我希望 Swagger 生成与代码 400 相同的响应对象,这是包含错误代码和错误消息的默认返回对象。

为什么 Swagger 采用与为代码 200 定义的返回对象相同的返回对象? 我期待 Swagger 会生成默认的返回对象。这是 Swagger 中的错误吗?

【问题讨论】:

  • 你解决过这个问题吗?

标签: java spring swagger-ui openapi


【解决方案1】:

这是一个不断出现的问题,因为 Java 方法只能有返回类型。您的方法的响应类型为 List&lt;LetterDTO&gt;,因此无论您返回什么 HTTP 状态,响应都将采用该结构。

为了解决这个问题,大多数人采用以下方法之一:

  • 返回类型为Object,实际返回了相应的响应对象。在这种情况下,用户必须依靠您的 API 文档来正确处理响应。
  • 另一种方法是在响应中使用自己的包装器。这也需要文档支持。
  • 第三种方法是让您的@RequestMapping() 选择器使用微调的选择,而不仅仅是通过端点路径。例如,也使用请求方法。但是,这样做的缺点是定义了新的 REST 端点。

但总的来说,没有简单的方法可以解决这个问题。


编辑:添加示例

让我们假设这些简单的对象作为您的数据结构。

@NoArgsConstructor @AllArgsConstructor
@Getter @Setter
private static class LetterDTO{
    String from;
}

@NoArgsConstructor @AllArgsConstructor
@Getter @Setter
private static class ErrorResp{
    String message;
}

然后,对于使用Object 作为返回值的第一种情况,您可以这样做:

@GetMapping(value = "letters/user/{userId}", produces = {"application/json"})
public Object getLettersForUser( @PathVariable( "userId") String input, HttpServletResponse resp ) {
    if( input.equalsIgnoreCase( "a" ) ) {
        resp.setStatus( 200 );
        return Arrays.asList( new LetterDTO( "A Friend" ) );
    }
    else{
        resp.setStatus( 415 ); //Invalid user
        return new ErrorResp( "Invalid user" );
    }
}

SpringMVC 方式甚至更好,即返回ResponseEntity

@GetMapping(value = "letters2/user/{userId}", produces = {"application/json"})
public ResponseEntity<Object> getLettersForUser2( @PathVariable( "userId") String input, HttpServletResponse resp ) {
    if( input.equalsIgnoreCase( "a" ) ) {
        return ResponseEntity.ok().body( Arrays.asList( new LetterDTO( "A Friend" ) ) );
    }
    else{
        return ResponseEntity.status( 415 ).body( Arrays.asList( new ErrorResp( "Invalid user" ) ) );
    }
}

现在必须明确使用您的类作为返回值。而不是Object,你返回一些你的结构作为包装器说:

@NoArgsConstructor @AllArgsConstructor
@Getter @Setter
private static class APIResponse{
    String message;
    Object data;
}

从这些示例中可以看出,您的 API 文档变得非常重要,因为很难从签名中收集每种情况下响应的内容 - 200、415 等。

【讨论】:

  • 但为什么 Swagger 会为代码 400 设置正确的默认返回类型?
  • Swagger 可能会在其注释中生成正确的响应类型,但您使用的是 SpringMVC,它不使用 Swagger 注释来生成实际响应。因此,您的 Swagger 注释仅用于文档目的。
  • 我不明白如何实现解决方案 1 和 2。是否可以展示这些解决方案的示例?或者提供一个现有示例的链接?
  • @user2023141 我添加了一些示例。希望他们有所帮助。
猜你喜欢
  • 2020-10-16
  • 2020-09-21
  • 2021-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多