I'm building a REST api starting with spring-boot-starter-webflux, but I'd also like the app to serve a swagger UI using a webjar. I need to tweak how the swagger UI initializes, so I've made a @Controller that returns a Mono to trigger rendering the view. The REST api also needs a customized jackson ObjectMapper with the snake case naming strategy enabled, among other things.
The Spring Boot Docs say that to accomplish this I should provide a @Configuration class that extends WebFluxConfigurer, but I should leave @EnableWebflux off. Here is what I have so far (Kotlin):
Config
@Configuration
@ComponentScan
class CommonConfig : WebFluxConfigurer {
override fun configureHttpMessageCodecs(configurer: ServerCodecConfigurer) {
configurer.defaultCodecs().jackson2JsonDecoder(
Jackson2JsonDecoder(objectMapper())
)
configurer.defaultCodecs().jackson2JsonEncoder(
Jackson2JsonEncoder(objectMapper())
)
}
private fun objectMapper() = Jackson2ObjectMapperBuilder
.json()
.propertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE)
.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.build<ObjectMapper>().also {
it.configOverride(BigDecimal::class.java).format = JsonFormat.Value.forShape(JsonFormat.Shape.STRING)
}
}
Controller
@Controller
class SwaggerController(
val swaggerConfig: SwaggerConfig
) {
@GetMapping("/swagger-ui")
fun swagger(model: Model): Mono<String> {
model.addAttribute("docUrl", swaggerConfig.docUrl)
return Mono.just("swagger-ui")
}
}
With this configuration, the swagger view renders fine, but when I try to post to one of my api endpoints, it complains about missing required properties (which is resolved by sending data with camel case instead of the snake case that I want). If I add @EnableWebFlux to my CommonConfig class, the custom object mapper gets picked up and I can send snake case data, but then the swagger view does not render. I get:
java.lang.IllegalStateException: Could not resolve view with name 'swagger-ui'.
at org.springframework.web.reactive.result.view.ViewResolutionResultHandler.lambda$resolveViews$3(ViewResolutionResultHandler.java:278) ~[spring-webflux-5.2.5.RELEASE.jar:5.2.5.RELEASE]
Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException:
This is presumably because there is no thymeleaf ViewResolver registered.
The docs do say that if I want to "take complete control of Spring WebFlux, you can add your own @Configuration annotated with @EnableWebFlux", which explains why there's no ViewResolver. But this isn't what I want. I want most of the webflux autoconfiguration, but I want to provide my own customized ObjectMapper. What is the correct way to do this?
I ended up 1) switching to FreeMarker and 2) needing to expose my custom ObjectMapper as a bean, though this came with its own problems.