d5-framework-springdoc
Gera a documentação OpenAPI da API REST do backend e a expõe através do Swagger UI, a partir de springdoc-openapi.
Como nenhuma classe da aplicação depende dele, o módulo pode ser declarado apenas em tempo de execução (<scope>runtime</scope>), como no tutorial. Os caminhos do Swagger UI e da documentação OpenAPI são liberados sem autenticação por SegurancaConfiguracaoAbstrata, que assume os caminhos padrão do springdoc-openapi.
SpringDocConfiguracao
Declara basic como o esquema de segurança da API, do tipo HTTP, e o aplica a toda a documentação gerada. É esse esquema que faz o Swagger UI exibir o botão Authorize e anexar o cabeçalho Authorization com usuário e senha a cada requisição, depois de autenticado. Também registra o OperationCustomizer que expande parâmetros @ModelAttribute, como o de pesquisa, em parâmetros individuais; veja Expansão da consulta no Swagger UI. Por fim, registra o ModelConverter que documenta objetos de valor de string como string; veja ObjetoValorStringConversor.
package dev.sauloaraujo.d5.framework.springdoc;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.annotations.enums.SecuritySchemeType;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.security.SecurityScheme;
@Configuration
@OpenAPIDefinition(security = @SecurityRequirement(name = "basic"))
@SecurityScheme(name = "basic", type = SecuritySchemeType.HTTP, scheme = "basic")
public class SpringDocConfiguracao {
@Bean
OperationCustomizer modelAttributeCustomizador() {
return new ModelAttributeCustomizador();
}
@Bean
ModelConverter objetoValorStringConversor() {
return new ObjetoValorStringConversor();
}
}
ObjetoValorStringConversor
Descreve como string, na documentação OpenAPI, todo tipo que estende ObjetoValorStringAbstrato. Esses objetos trafegam como uma string simples, mas o springdoc-openapi desconhece essa serialização e, sem o conversor, documentaria o tipo a partir de seus getters — por exemplo, um identificador com o método getUnidadeFederativa() apareceria como um objeto com a propriedade unidadeFederativa, tanto nos parâmetros de caminho quanto no corpo das requisições. Veja Serialização de objetos de valor.
package dev.sauloaraujo.d5.framework.springdoc;
import java.util.Iterator;
import org.jspecify.annotations.Nullable;
import dev.sauloaraujo.d5.framework.dominio.impl.ObjetoValorStringAbstrato;
import io.swagger.v3.core.converter.AnnotatedType;
import io.swagger.v3.core.converter.ModelConverter;
import io.swagger.v3.core.converter.ModelConverterContext;
import io.swagger.v3.core.util.Json;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
/**
* Objetos de valor de string são serializados como string simples; portanto, a
* documentação não deve expor suas propriedades.
*/
class ObjetoValorStringConversor implements ModelConverter {
@Override
public @Nullable Schema<?> resolve(AnnotatedType tipo, ModelConverterContext contexto,
Iterator<ModelConverter> cadeia) {
var tipoJava = Json.mapper().constructType(tipo.getType());
if (tipoJava != null && ObjetoValorStringAbstrato.class.isAssignableFrom(tipoJava.getRawClass())) {
return new StringSchema();
}
return cadeia.hasNext() ? cadeia.next().resolve(tipo, contexto, cadeia) : null;
}
}
SpringDocEnvironmentPostProcessor
Habilita, por padrão, o cabeçalho de CSRF nas requisições de escrita do Swagger UI, definindo a propriedade springdoc.swagger-ui.csrf.enabled como true; veja CSRF no Swagger UI. A propriedade é adicionada como a fonte de propriedades de menor precedência, de modo que a aplicação pode sobrescrevê-la — por exemplo, em application.properties.
package dev.sauloaraujo.d5.framework.springdoc;
import java.util.Map;
import org.springframework.boot.EnvironmentPostProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MapPropertySource;
public class SpringDocEnvironmentPostProcessor implements EnvironmentPostProcessor {
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
environment.getPropertySources().addLast(
new MapPropertySource("d5-framework-springdoc", Map.of("springdoc.swagger-ui.csrf.enabled", true)));
}
}
É registrado em META-INF/spring.factories, e não em META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports como SpringDocConfiguracao, porque não é uma autoconfiguração: o Spring Boot executa os EnvironmentPostProcessor ao preparar o Environment, antes de criar o contexto da aplicação — e, portanto, antes de processar qualquer autoconfiguração —, e os descobre apenas através de spring.factories.