use*_*141 4 java spring swagger-ui openapi
我在 SpringBoot 项目中使用 OpenApi 3 来生成 Swagger html 页面。
POM.xml 中的依赖项:
Run Code Online (Sandbox Code Playgroud)<dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-ui</artifactId> <version>1.5.12</version> </dependency>
在控制器类中,我在方法上方定义了以下注释。
@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(
...
)
Run Code Online (Sandbox Code Playgroud)
Swagger UI 的输出显示代码 200 的正确响应,它是 LetterDTO 对象的列表。
但代码 401 的响应还显示 LetterDTO 对象的列表。不过,我没有为代码 401 定义任何响应对象。我期望 Swagger 生成与代码 400 相同的响应对象,它是包含错误代码和错误消息的默认返回对象。
为什么 Swagger 采用与代码 200 定义的返回对象相同的返回对象?我期望 Swagger 会生成默认的返回对象。这是 Swagger 中的错误吗?
我通常这样配置 API 响应:
@ApiResponse(responseCode = "200", description = "OK")
@ApiResponse(responseCode = "400", description = "Invalid request", content = @Content)
Run Code Online (Sandbox Code Playgroud)
如果未content指定,则使用相应控制器方法的返回类型。content = @Content告诉 Swagger 响应中没有内容。
这@ApiGetOne就是 Swagger 将显示的内容(屏幕截图来自不同的 DTO 类):
为了简单性和可重用性,我通常将它们包装在可重用的帮助器注释中,这样我的端点就没有那么多注释,并且我不需要ResponseEntity在控制器中使用注释,例如:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@RequestMapping(method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
@ApiResponse(responseCode = "200", description = "OK")
@ApiResponse(responseCode = "400", description = "Invalid request", content = @Content)
@ApiResponse(responseCode = "500", description = "Internal error", content = @Content)
public @interface ApiGet {
@AliasFor(annotation = RequestMapping.class)
String[] value() default {};
@AliasFor(annotation = RequestMapping.class)
String[] path() default {};
}
Run Code Online (Sandbox Code Playgroud)
您还可以使用更多 API 响应来扩展这些注释,例如,要为某些端点添加 404,请创建另一个注释@ApiGet:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@ApiGet
@ApiResponse(responseCode = "404", description = "Not found", content = @Content)
public @interface ApiGetOne {
@AliasFor(annotation = ApiGet.class)
String[] value() default {};
@AliasFor(annotation = ApiGet.class)
String[] path() default {};
}
Run Code Online (Sandbox Code Playgroud)
最后,在任何端点上使用它们(使用 Java 17):
public record HelloWorldDto(String recipientName) {
public String getMessage() {
return "Hello, %s".formatted(recipientName);
}
}
Run Code Online (Sandbox Code Playgroud)
public record ErrorDto(String message) {
}
Run Code Online (Sandbox Code Playgroud)
@RestController
@RequestMapping("api/test")
@Tag(name = "Demo", description = "Endpoints for testing")
public class DemoController {
...
@ApiGet("/hello")
public HelloWorldDto sayHello() {
return new HelloWorldDto("stranger");
}
@ApiGetOne("/hello/{id}")
public HelloWorldDto sayHelloWithParam(@PathVariable int id) {
final var person = myPersonRepo.getById(id); // might throw a NotFoundException which is mapped to 404 status code
return new HelloWorldDto(person.name());
}
}
Run Code Online (Sandbox Code Playgroud)
将异常映射到自定义错误响应:
@ControllerAdvice
public class ErrorHandler {
private static final Logger log = LoggerFactory.getLogger(ErrorHandler.class);
@ExceptionHandler
public ResponseEntity<ErrorDto> handle(Exception exception) {
log.error("Internal server error occurred", exception);
return response(HttpStatus.INTERNAL_SERVER_ERROR, "Unknown error occurred.");
}
@ExceptionHandler
public ResponseEntity<ErrorDto> handle(NotFoundException exception) {
return response(HttpStatus.NOT_FOUND, exception.getMessage());
}
private ResponseEntity<ErrorDto> response(HttpStatus status, String message) {
return ResponseEntity
.status(status)
.body(new ErrorDto(message));
}
}
Run Code Online (Sandbox Code Playgroud)
我非常喜欢这个设置,因为
ResponseEntity控制器方法@ControllerAdvice可重用错误处理的中心点更新2022/04/20
只需修复一个错误,即我们有一个返回图像而不是 JSON 的端点。在这种情况下,为了防止HttpMessageNotWritableException: No converter for [class ErrorDto] with preset Content-Type 'image/jpeg',您需要Accept像这样检查请求的标头(使用标头作为后备):
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorDto> handle(final Exception exception, final WebRequest webRequest) {
return createResponse(HttpStatus.INTERNAL_SERVER_ERROR, "Some error", webRequest);
}
protected ResponseEntity<ErrorDto> createResponse(final HttpStatus httpStatus,
final String message,
final WebRequest webRequest) {
final var accepts = webRequest.getHeader(HttpHeaders.ACCEPT);
if (!MediaType.APPLICATION_JSON_VALUE.equals(accepts)) {
return ResponseEntity.status(httpStatus)
.header("my-error", message)
.build();
}
return ResponseEntity
.status(status)
.body(new ErrorDto(message));
}
Run Code Online (Sandbox Code Playgroud)