¡Hola a todos! En este artículo, me gustaría considerar las herramientas de documentación en enfoques fundamentalmente diferentes para el desarrollo de API REST, a saber, para las herramientas CodeFirst - SpringRestDocs (así como su complemento SpringAutoRestDocs) y para las herramientas del ecosistema ApiFirst - Swagger (Open-Api).
Descargo de responsabilidad: no entraré en los detalles del holivar sobre qué es mejor que CodeFirst o ApiFirst, pero intentaré demostrar la posible práctica de la documentación en ambas versiones.
CodeFirst más 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 ), .
, "" .
La capacidad de personalizar fragmentos y restricciones.
Lo que ofrece Swagger:
Un solo artefacto en todas las etapas de desarrollo.
La documentación y el modelo de datos siempre están sincronizados con el código, ya que el código se genera en base a un contrato.
La capacidad de usar una interfaz de usuario que contiene la misma información que en el contrato con la capacidad de enrutar solicitudes.
Cualquiera que sea la ruta de desarrollo que elija, las herramientas anteriores casi siempre le permitirán mantener actualizada la documentación estrechamente relacionada con el código de producción.