如何在 spring-boot-starter-data-rest 中为 Swagger 文档实现 snake_case?
How to implement snake_case for Swagger documentation in spring-boot-starter-data-rest?
我在 spring-boot-starter-data-rest 项目的文档中使用 swagger。在 application.properties 文件中,我配置了: spring.jackson.property-naming-strategy=SNAKE_CASE 命名策略,但不幸的是,我在 swagger 文档中使用了驼峰命名法。但是,如果我将项目从 spring-boot-starter-data-rest 更改为 spring-boot-starter-web,则相同的配置会起作用。以下是我在 spring boot 2.1.1.RELEASE 中使用的依赖项。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE
包 com.example.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
SwaggerConfig
package com.example.demo1;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import
org.springframework.data.web.config.SpringDataJacksonConfiguration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
@Configuration
@EnableSwagger2
@Import({SpringDataJacksonConfiguration.class})
public class SwaggerConfig {
public static ApiInfo metaData(String info) {
return new ApiInfo(info,
"Th",
"1.0", "httn.html",
new Contact("Thd", "", "thoom"), "decense",
"https", new ArrayList());
}
@Bean
public Docket cashFlowApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("-caching").select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo1"))
.paths(PathSelectors.any())
.build()
.apiInfo(SwaggerConfig.metaData("BOcPI"));
}
}
商店控制器
package com.example.demo1;
import java.util.Arrays;
import java.util.List;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
public class StoreController {
@GetMapping(value = "/v1/storeMap")
@ApiOperation(value = "Returns the list of stores", notes = "Returns the list of stores with pagination.")
@ApiResponses(value = {@ApiResponse(code = 200, message = "Successfully retrieved the stores list"),
@ApiResponse(code = 204, message = "No content"), @ApiResponse(code = 206, message = "Partial Content"),
@ApiResponse(code = 401, message = "You are not authorized to view the resource"),
@ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
@ApiResponse(code = 404, message = "The resource you were trying to reach is not found"),
@ApiResponse(code = 500, message = "A technical error happened")})
public ResponseEntity<Store> getStore(
@RequestParam(name = "country_code", required = false) @ApiParam(value = "the code)") String countryCode
) {
return ResponseEntity.ok(new Store(1,"ZZ"));
}
}
现在使用此配置,在 API POST 方法中需要 snake_case 并且在文档中,swagger 显示驼峰式。我没有选择从 snake_case 更改为 camelCase 或 spring-boot-starter-data-rest 到 spring-boot-starter-web.
我找到了解决方案,问题出在对象映射器上:
@Configuration
public class ObjectMapperAutoConfiguration implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper objectMapper = null;
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter ) {
MappingJackson2HttpMessageConverter jacksonConverter =
((MappingJackson2HttpMessageConverter) converter);
if (objectMapper == null) {
objectMapper = jacksonConverter.getObjectMapper();
} else {
jacksonConverter.setObjectMapper(objectMapper);
}
}
}
}
}
我还有一个 Spring Boot + Swagger 项目有同样的问题,但是使用 Spring io.swagger 包而不使用 Springfox 库并使用 Gradle 插件 com.benjaminsproule.swagger 生成文档。所以为了解决这个问题,我添加了这一行 ModelConverters.getInstance().addConverter(new ModelResolver(objectMapper))
(这是一个 hack,我没有使用官方 API,但它有效):
package com.company.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.converter.ModelConverters;
import io.swagger.jackson.ModelResolver;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.*;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class SwaggerConfig extends ResourceConfig {
@Autowired ObjectMapper objectMapper;
@PostConstruct public void configureSwagger() {
// Available at localhost:port/swagger.json
this.register(ApiListingResource.class);
this.register(SwaggerSerializers.class);
BeanConfig config = new BeanConfig();
config.setConfigId("api-springboot-jersey-swagger");
config.setTitle("Company API");
config.setResourcePackage("com.company.api");
config.setPrettyPrint(true);
config.setScan(true);
// With this hack we inject into Swagger the same object mapper
// used by Spring (spring.jackson.property-naming-strategy)
ModelConverters.getInstance().addConverter(new ModelResolver(objectMapper));
}
}
我在 spring-boot-starter-data-rest 项目的文档中使用 swagger。在 application.properties 文件中,我配置了: spring.jackson.property-naming-strategy=SNAKE_CASE 命名策略,但不幸的是,我在 swagger 文档中使用了驼峰命名法。但是,如果我将项目从 spring-boot-starter-data-rest 更改为 spring-boot-starter-web,则相同的配置会起作用。以下是我在 spring boot 2.1.1.RELEASE 中使用的依赖项。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>demo1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>2.9.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.jackson.property-naming-strategy=SNAKE_CASE
包 com.example.demo1;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
SpringApplication.run(Demo1Application.class, args);
}
}
SwaggerConfig
package com.example.demo1;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import
org.springframework.data.web.config.SpringDataJacksonConfiguration;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.Contact;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;
import java.util.ArrayList;
@Configuration
@EnableSwagger2
@Import({SpringDataJacksonConfiguration.class})
public class SwaggerConfig {
public static ApiInfo metaData(String info) {
return new ApiInfo(info,
"Th",
"1.0", "httn.html",
new Contact("Thd", "", "thoom"), "decense",
"https", new ArrayList());
}
@Bean
public Docket cashFlowApi() {
return new Docket(DocumentationType.SWAGGER_2).groupName("-caching").select()
.apis(RequestHandlerSelectors.basePackage("com.example.demo1"))
.paths(PathSelectors.any())
.build()
.apiInfo(SwaggerConfig.metaData("BOcPI"));
}
}
商店控制器
package com.example.demo1;
import java.util.Arrays;
import java.util.List;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@CrossOrigin
public class StoreController {
@GetMapping(value = "/v1/storeMap")
@ApiOperation(value = "Returns the list of stores", notes = "Returns the list of stores with pagination.")
@ApiResponses(value = {@ApiResponse(code = 200, message = "Successfully retrieved the stores list"),
@ApiResponse(code = 204, message = "No content"), @ApiResponse(code = 206, message = "Partial Content"),
@ApiResponse(code = 401, message = "You are not authorized to view the resource"),
@ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
@ApiResponse(code = 404, message = "The resource you were trying to reach is not found"),
@ApiResponse(code = 500, message = "A technical error happened")})
public ResponseEntity<Store> getStore(
@RequestParam(name = "country_code", required = false) @ApiParam(value = "the code)") String countryCode
) {
return ResponseEntity.ok(new Store(1,"ZZ"));
}
}
现在使用此配置,在 API POST 方法中需要 snake_case 并且在文档中,swagger 显示驼峰式。我没有选择从 snake_case 更改为 camelCase 或 spring-boot-starter-data-rest 到 spring-boot-starter-web.
我找到了解决方案,问题出在对象映射器上:
@Configuration
public class ObjectMapperAutoConfiguration implements WebMvcConfigurer {
@Override
public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
ObjectMapper objectMapper = null;
for (HttpMessageConverter converter : converters) {
if (converter instanceof MappingJackson2HttpMessageConverter ) {
MappingJackson2HttpMessageConverter jacksonConverter =
((MappingJackson2HttpMessageConverter) converter);
if (objectMapper == null) {
objectMapper = jacksonConverter.getObjectMapper();
} else {
jacksonConverter.setObjectMapper(objectMapper);
}
}
}
}
}
我还有一个 Spring Boot + Swagger 项目有同样的问题,但是使用 Spring io.swagger 包而不使用 Springfox 库并使用 Gradle 插件 com.benjaminsproule.swagger 生成文档。所以为了解决这个问题,我添加了这一行 ModelConverters.getInstance().addConverter(new ModelResolver(objectMapper))
(这是一个 hack,我没有使用官方 API,但它有效):
package com.company.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import io.swagger.converter.ModelConverters;
import io.swagger.jackson.ModelResolver;
import io.swagger.jaxrs.config.BeanConfig;
import io.swagger.jaxrs.listing.*;
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
@Component
public class SwaggerConfig extends ResourceConfig {
@Autowired ObjectMapper objectMapper;
@PostConstruct public void configureSwagger() {
// Available at localhost:port/swagger.json
this.register(ApiListingResource.class);
this.register(SwaggerSerializers.class);
BeanConfig config = new BeanConfig();
config.setConfigId("api-springboot-jersey-swagger");
config.setTitle("Company API");
config.setResourcePackage("com.company.api");
config.setPrettyPrint(true);
config.setScan(true);
// With this hack we inject into Swagger the same object mapper
// used by Spring (spring.jackson.property-naming-strategy)
ModelConverters.getInstance().addConverter(new ModelResolver(objectMapper));
}
}