packagecn.tofuwine.swaggerui.controller;importjava.util.Collection;importcn.tofuwine.swaggerui.exception.BookNotFoundException;importcn.tofuwine.swaggerui.model.Book;importcn.tofuwine.swaggerui.repository.BookRepository;importjakarta.validation.Valid;importjakarta.validation.constraints.NotNull;importorg.springframework.http.HttpStatus;importorg.springframework.web.bind.annotation.DeleteMapping;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PatchMapping;importorg.springframework.web.bind.annotation.PathVariable;importorg.springframework.web.bind.annotation.PostMapping;importorg.springframework.web.bind.annotation.PutMapping;importorg.springframework.web.bind.annotation.RequestBody;importorg.springframework.web.bind.annotation.RequestMapping;importorg.springframework.web.bind.annotation.RequestMethod;importorg.springframework.web.bind.annotation.ResponseStatus;importorg.springframework.web.bind.annotation.RestController;/**
* The type Book controller.
*/@RestController@RequestMapping("/api/book")publicclassBookController{/**
* The Repository.
*/privatefinalBookRepositoryrepository;/**
* Instantiates a new Book controller.
*
* @param repository the repository
*/publicBookController(BookRepositoryrepository){this.repository=repository;}/**
* Find by id book.
*
* @param id the id
* @return the book
*/@GetMapping("/{id}")publicBookfindById(@PathVariablelongid){returnrepository.findById(id).orElseThrow(BookNotFoundException::new);}/**
* Find books collection.
*
* @return the collection
*/@GetMapping("/")publicCollection<Book>findBooks(){returnrepository.getBooks();}/**
* Update book.
*
* @param id the id
* @param book the book
* @return the book
*/@PutMapping("/{id}")@ResponseStatus(HttpStatus.OK)publicBookupdateBook(@PathVariable("id")finalStringid,@RequestBodyfinalBookbook){returnbook;}/**
* Patch book.
*
* @param id the id
* @param book the book
* @return the book
*/@PatchMapping("/{id}")@ResponseStatus(HttpStatus.OK)publicBookpatchBook(@PathVariable("id")finalStringid,@RequestBodyfinalBookbook){returnbook;}/**
* Post book.
*
* @param book the book
* @return the book
*/@PostMapping("/")@ResponseStatus(HttpStatus.CREATED)publicBookpostBook(@NotNull@Valid@RequestBodyfinalBookbook){returnbook;}/**
* Head book.
*
* @return the book
*/@RequestMapping(method=RequestMethod.HEAD,value="/")@ResponseStatus(HttpStatus.OK)publicBookheadBook(){returnnewBook();}/**
* Delete book long.
*
* @param id the id
* @return the long
*/@DeleteMapping("/{id}")@ResponseStatus(HttpStatus.OK)publiclongdeleteBook(@PathVariablefinallongid){returnid;}}
importio.swagger.v3.oas.models.Components;importio.swagger.v3.oas.models.OpenAPI;importio.swagger.v3.oas.models.info.Info;importio.swagger.v3.oas.models.info.License;importorg.springframework.beans.factory.annotation.Value;importorg.springframework.context.annotation.Bean;importorg.springframework.context.annotation.Configuration;@ConfigurationpublicclassOpenAPIConfig{@BeanpublicOpenAPIcustomOpenAPI(@Value("${app.version}")Stringversion){returnnewOpenAPI().components(newComponents()).info(newInfo().title("Book API").version(version).description("API documentation for Book endpoints").license(newLicense().name("Apache 2.0").url("http://springdoc.org")));}}
配置完成后,重新启动应用,您将看到文档的标题、描述等信息已经更新:
接下来,我们需要为具体的 API 接口添加更详细的描述信息。Springdoc OpenAPI 提供了两种主要方式:传统的注解式写法和更便捷的 Javadoc 写法。
文档增强方法
方法一:使用 Swagger 注解
Springdoc OpenAPI 提供了一系列注解,可以直接添加到 Controller 类、方法和参数上,用于增强 API 文档的详细程度。以下是几个常用的注解:
importio.swagger.v3.oas.annotations.Operation;importio.swagger.v3.oas.annotations.Parameter;importio.swagger.v3.oas.annotations.media.Content;importio.swagger.v3.oas.annotations.media.Schema;importio.swagger.v3.oas.annotations.responses.ApiResponse;importio.swagger.v3.oas.annotations.responses.ApiResponses;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.PathVariable;@Operation(summary="Get a book by its id")@ApiResponses(value={@ApiResponse(responseCode="200",description="Found the book",content={@Content(mediaType="application/json",schema=@Schema(implementation=Book.class))}),@ApiResponse(responseCode="400",description="Invalid id supplied",content=@Content),@ApiResponse(responseCode="404",description="Book not found",content=@Content)})@GetMapping("/{id}")publicBookfindById(@Parameter(description="id of book to be searched")@PathVariablelongid){returnrepository.findById(id).orElseThrow(BookNotFoundException::new);}
方法二:使用 Javadoc 注释(推荐)
注解式写法虽然功能强大,但会导致代码侵入性强,大量注解可能会淹没实际的业务代码。Springdoc OpenAPI 提供了一种更优雅的解决方案 —— 使用标准的 Javadoc 注释来生成 API 文档。
importorg.springframework.core.convert.ConversionFailedException;importorg.springframework.http.HttpStatus;importorg.springframework.http.ProblemDetail;importorg.springframework.web.bind.annotation.ExceptionHandler;importorg.springframework.web.bind.annotation.ResponseStatus;importorg.springframework.web.bind.annotation.RestControllerAdvice;importorg.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;@RestControllerAdvicepublicclassGlobalControllerExceptionHandler{@ExceptionHandler(ConversionFailedException.class)@ResponseStatus(HttpStatus.BAD_REQUEST)publicProblemDetailhandleConversion(RuntimeExceptione){ProblemDetailproblemDetail=ProblemDetail.forStatusAndDetail(HttpStatus.BAD_REQUEST,e.getMessage());problemDetail.setTitle("Bookmark is Not Found");problemDetail.setProperty("errorCategory","Generic Exception");problemDetail.setProperty("timestamp",Instant.now());returnproblemDetail;}@ExceptionHandler(BookNotFoundException.class)@ResponseStatus(HttpStatus.NOT_FOUND)publicProblemDetailhandleBookNotFound(RuntimeExceptione){ProblemDetailproblemDetail=ProblemDetail.forStatusAndDetail(HttpStatus.NOT_FOUND,e.getMessage());problemDetail.setTitle("Book Not Found");problemDetail.setProperty("errorCategory","Generic Exception");problemDetail.setProperty("timestamp",Instant.now());returnproblemDetail;}}
分页参数的自动文档生成
Springdoc OpenAPI 自 1.6.0 版本起,提供了对 Spring Data JPA 分页参数的原生支持。当您在接口中使用 Pageable 参数时,系统会自动在文档中添加 page、size 和 sort 等查询参数的说明: