Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I did not change anything manually but for some reason, it is showing the wrong parameter name.
return new Docket(DocumentationType.SWAGGER_2)
.groupName("Rest API")
.securitySchemes(Collections.singletonList(new BasicAuth(BASIC_AUTH)))
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.paths(s -> oneOf(
"/some/**",
"/search-controller/**").test(s))
.build();
And the pom is
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>2.9.0</version>
</dependency>
And the controller is something like below
@RequestMapping(method = RequestMethod.GET)
public HttpEntity<?> findAll(@RequestParam(value = "countryIsoAlpha2", required = false) final String countryKey, final Pageable pageable){
–
https://github.com/springfox/springfox/issues/755#issuecomment-393378205
Below is an example for creating a rule that automatically provides a convention for configuring Pageable type.
@Configuration
public class SwaggerConfig {
@Bean
public AlternateTypeRuleConvention pageableConvention(
final TypeResolver resolver) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
@Override
public List<AlternateTypeRule> rules() {
return Arrays.asList(
newRule(resolver.resolve(Pageable.class), resolver.resolve(pageableMixin()))
private Type pageableMixin() {
return new AlternateTypeBuilder()
.fullyQualifiedClassName(
String.format("%s.generated.%s",
Pageable.class.getPackage().getName(),
Pageable.class.getSimpleName()))
.withProperties(Arrays.asList(
property(Integer.class, "page"),
property(Integer.class, "size"),
property(String.class, "sort")
.build();
private AlternateTypePropertyBuilder property(Class<?> type, String name) {
return new AlternateTypePropertyBuilder()
.withName(name)
.withType(type)
.withCanRead(true)
.withCanWrite(true);
–
–
Here's my expansion on the previous two examples. It also includes ApiParam annotation values and is configurable through SpringDataWebProperties settings.
Before Field Expansion:
After Field Expansion:
There are several ways to use Swagger AlternateTypeRules. They can be added directly to the Swagger Docket, added as a bean in several ways. Inspired by the above examples, here's how I did it, using an Annotation Proxy (show below) to include the additional ApiParam data:
@EnableSwagger2
@Configuration
public class SwaggerConfiguration {
@Bean
public AlternateTypeRuleConvention springDataWebPropertiesConvention(final SpringDataWebProperties webProperties) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; }
@Override
public List<AlternateTypeRule> rules() {
return singletonList(
newRule(Pageable.class, pageableDocumentedType(webProperties.getPageable(), webProperties.getSort()))
private Type pageableDocumentedType(SpringDataWebProperties.Pageable pageable, SpringDataWebProperties.Sort sort) {
final String firstPage = pageable.isOneIndexedParameters() ? "1" : "0";
return new AlternateTypeBuilder()
.fullyQualifiedClassName(fullyQualifiedName(Pageable.class))
.property(property(pageable.getPageParameter(), Integer.class, ImmutableMap.of(
"value", "Page " + (pageable.isOneIndexedParameters() ? "Number" : "Index"),
"defaultValue", firstPage,
"allowableValues", String.format("range[%s, %s]", firstPage, Integer.MAX_VALUE),
"example", firstPage
.property(property(pageable.getSizeParameter(), Integer.class, ImmutableMap.of(
"value", "Page Size",
"defaultValue", String.valueOf(pageable.getDefaultPageSize()),
"allowableValues", String.format("range[1, %s]", pageable.getMaxPageSize()),
"example", "5"
.property(property(sort.getSortParameter(), String[].class, ImmutableMap.of(
"value", "Page Multi-Sort: fieldName,(asc|desc)"
.build();
private String fullyQualifiedName(Class<?> convertedClass) {
return String.format("%s.generated.%s", convertedClass.getPackage().getName(), convertedClass.getSimpleName());
private AlternateTypePropertyBuilder property(String name, Class<?> type, Map<String, Object> parameters) {
return new AlternateTypePropertyBuilder()
.withName(name)
.withType(type)
.withCanRead(true)
.withCanWrite(true)
.withAnnotations(Collections.singletonList(AnnotationProxy.of(ApiParam.class, parameters)));
In order to add the additional data like defaultValue and allowableValues, you have to use the .withAnnotations() method, which requires an Annotation Proxy. There are several available, here's mine (using lombok):
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@Accessors(fluent = true)
public class AnnotationProxy implements Annotation, InvocationHandler {
@Getter
private final Class<? extends Annotation> annotationType;
private final Map<String, Object> values;
public static <A extends Annotation> A of(Class<A> annotation, Map<String, Object> values) {
return (A) Proxy.newProxyInstance(annotation.getClassLoader(),
new Class[]{annotation},
new AnnotationProxy(annotation, new HashMap<String, Object>(values) {{
put("annotationType", annotation); // Required because getDefaultValue() returns null for this call
}}));
public Object invoke(Object proxy, Method method, Object[] args) {
return values.getOrDefault(method.getName(), method.getDefaultValue());
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.archisoft.mtx.controller"))
.paths(regex("/api.*"))
.build()
.apiInfo(apiInfo());
private ApiInfo apiInfo() {
ApiInfo apiInfo = new ApiInfo(
"Backend API Services",
"Backend APIs for Business to Business",
"V1.0",
"Terms of service",
"Sadun | Tharanga email",
"Archisoft Global",
"Archisoft URL");
return apiInfo;
@Bean
public AlternateTypeRuleConvention pageableConvention(
final TypeResolver resolver,
final RepositoryRestConfiguration restConfiguration) {
return new AlternateTypeRuleConvention() {
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
@Override
public List<AlternateTypeRule> rules() {
return singletonList(
newRule(resolver.resolve(Pageable.class), resolver.resolve(pageableMixin()))
private Type pageableMixin() {
return new AlternateTypeBuilder()
.fullyQualifiedClassName(
String.format("%s.generated.%s",
Pageable.class.getPackage().getName(),
Pageable.class.getSimpleName()))
.withProperties(Stream.of(
property(Integer.class, "page"),
property(Integer.class, "size"),
property(String.class, "sort")
).collect(toList()))
.build();
private AlternateTypePropertyBuilder property(Class<?> type, String name) {
return new AlternateTypePropertyBuilder()
.withName(name)
.withType(type)
.withCanRead(true)
.withCanWrite(true);
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.