Hello everyone! In this article, I would like to consider documentation tools in fundamentally different approaches to REST API development, namely, for CodeFirst - SpringRestDocs tools (as well as its SpringAutoRestDocs add-on) and for ApiFirst - Swagger ecosystem tools (Open-Api).
Disclaimer: I will not go into the details of the holivar on what is better than CodeFirst or ApiFirst, but I will try to demonstrate the possible practice of documentation in both versions.
CodeFirst plus SpringAutoRestDocs
SpringRestDocs , (, ..) . , , - , .. SpringAutoRestDocs, JSR Spring , Javadoc, .
Swagger PetStore ( , ) (addPet
, deletePet
, getPetById
). getPetById
:
@RestController
@RequiredArgsConstructor
public class PetController implements PetApi {
private final PetRepository petRepository;
@Override
public ResponseEntity<Pet> getPetById(Long petId) {
return new ResponseEntity<>(petRepository.getPetById(petId), HttpStatus.OK);
}
//
}
.
:
@Before
void setUp() throws Exception {
this.mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.alwaysDo(prepareJackson(objectMapper, new TypeMapping()))
.alwaysDo(commonDocumentation())
.apply(documentationConfiguration(restDocumentation)
.uris().withScheme("http").withHost("localhost").withPort(8080)
.and()
.snippets().withTemplateFormat(TemplateFormats.asciidoctor())
.withDefaults(curlRequest(), httpRequest(), httpResponse(),
requestFields(), responseFields(), pathParameters(),
requestParameters(), description(), methodAndPath(),
section(), links(), embedded(), authorization(DEFAULT_AUTHORIZATION),
modelAttribute(requestMappingHandlerAdapter.getArgumentResolvers())))
.build()
}
protected RestDocumentationResultHandler commonDocumentation(Snippet... snippets) {
return document("rest-auto-documentation/{class-name}/{method-name}", commonResponsePreprocessor(), snippets)
}
protected OperationResponsePreprocessor commonResponsePreprocessor() {
return preprocessResponse(replaceBinaryContent(), limitJsonArrayLength(objectMapper), prettyPrint())
}
MockMVC
WebApplicationContext
@SpringBootTest
prepareJackson(objectMapper, new TypeMapping())
ResultHandler
, pojo
withDefaults(curlRequest(), httpRequest(), ..)
,
commonDocumentation()
, build , .
:
@Test
void getPetTest() {
//given
def petId = 1L
//when
def resultActions = mockMvc
.perform(RestDocumentationRequestBuilders
.get("/pet/{petId}", petId)
.header("Accept", "application/json"))
//then
resultActions
.andExpect(status().isOk())
.andExpect(content().string(objectMapper.writeValueAsString(buildReturnPet())))
}
MockMvc
, .
:
"" auto-section.adoc
( , MockMVC
) index.adoc
API. :
SpringAutoRestDocs SpringRestDocs.
- org/springframework/restdocs/templates/asciidoctor
default-
.
- org/springframework/restdocs/constraints
DefaultConstraintDescriptions.properties
.
ApiFirst Swagger
(1, 2) swagger - open-source , OpenApi Specification , REST api.
Swagger
Swagger - OpenApi Specification Swagger Tools.
OpenApi Specification - REST API. YAML JSON. , .
Swagger Tools - , - REST api. : Swagger Editor( , ), Swagger UI( API), Swagger Codegen( - , )
- : (swagger-library), swagger-ui(swagger-webjar-ui-starter), (spring-auto-rest-docs).
Swagger Swagger PetStore SpringAutoRestDocs .
build.gradle :
server stub Swagger OpenApi Generator.
OpenApi Generator
:
openApiGenerate {
generatorName = 'spring'
inputSpec = specFile
outputDir = "${project.projectDir}/"
id = "${artifactId}"
groupId = projectPackage
ignoreFileOverride = ignoreFile
apiPackage = "${projectPackage}.rest.api"
invokerPackage = "${projectPackage}.rest.invoker"
modelPackage = "${projectPackage}.rest.model"
configOptions = [
dateLibrary : 'java8',
hideGenerationTimestamp: 'true',
interfaceOnly : 'true',
delegatePattern : 'false',
configPackage : "${projectPackage}.configuration"
]
}
openApiGenerate
generatorName -
Spring ( spring)
dateLibrary - (joda, JSR-310 ..)
-
:
task codegen(dependsOn: ['openApiGenerate', 'copySpecs'])
compileJava.dependsOn(codegen)
compileJava.mustRunAfter(codegen)
, UI :
task copySpecs(type: Copy) {
from("${project.projectDir}/specs")
into("${project.projectDir}/src/main/resources/META-INF/specs")
}
Swagger-ui :
Rest-docs API:
UI :
@RestController
@RestController
@RequiredArgsConstructor
public class PetController implements PetApi {
private final PetRepository petRepository;
@Override
public ResponseEntity<Pet> getPetById(Long petId) {
return new ResponseEntity<>(petRepository.getPetById(petId), HttpStatus.OK);
}
//
}
swagger:
ui:
indexHandler:
enabled: true
resourceHandler: "/api/**"
apis:
- url: http://localhost:8080/specs/some_swagger.yaml
name: My api
:
Server stub - API.
Swagger UI - API UI:
:
SpringAutoRestDocs:
( index.html ), .
, "" .
The ability to customize snippets and restrictions.
What Swagger gives:
A single artifact at all stages of development.
The documentation and data model is always in sync with the code, as the code is generated based on a contract.
The ability to use a UI that contains all the same information as in the contract with the ability to route requests.
Whichever development path you choose, the tools above will almost always allow you to keep up-to-date documentation closely related to the production code.