springfox: Springfox 3.0.0 generates 3 classes instead of 1 (Class, ClassReq, ClassRes) and ClassReq is missing required fields

  • What version of the library are you using? 3.0.0

For a single class 3.0.0 generates 3 classes: the original class, one with the Req and one with the Res postfix. Req seems to be used if @RequestBody is being used. Res I suppose when @ResponseBody is in use (do not use it, class is still generated). The original Class name is used when the class is present on another object.

This makes it impossible to pass a response object to a request API in Java and other other languages that rely on nominal typing because they are completely separate classes.

Even worse: it seems that the ParsedBusinessScheduleReq object does not properly generate the required fields. Below is the used class. Take a look at the required field which is absent for the ParsedBusinessScheduleReq object.

@ApiModel(value = "ParsedBusinessSchedule")
data class OpeningHours(
    val daysInWeek: List<WeeklyOpeningHours> = emptyList(),
    val daysInYear: List<YearlyOpeningHours> = emptyList(),
    val winterSeason: WinterSeason? = null,
    val closedOnHoliday: Boolean = true
)
{
  "ParsedBusinessSchedule": {
    "type": "object",
    "required": [
      "closedOnHoliday",
      "daysInWeek",
      "daysInYear"
    ],
    "properties": {
      "closedOnHoliday": {
        "type": "boolean"
      },
      "daysInWeek": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/WeeklyOpeningHours"
        }
      },
      "daysInYear": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/YearlyOpeningHours"
        }
      },
      "winterSeason": {
        "$ref": "#/definitions/WinterSeason"
      }
    }
  },
  "ParsedBusinessScheduleReq": {
    "type": "object",
    "properties": {
      "closedOnHoliday": {
        "type": "boolean"
      },
      "daysInWeek": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/WeeklyOpeningHoursReq"
        }
      },
      "daysInYear": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/YearlyOpeningHoursReq"
        }
      },
      "winterSeason": {
        "$ref": "#/definitions/WinterSeasonReq"
      }
    }
  },
  "ParsedBusinessScheduleRes": {
    "type": "object",
    "required": [
      "closedOnHoliday",
      "daysInWeek",
      "daysInYear"
    ],
    "properties": {
      "closedOnHoliday": {
        "type": "boolean"
      },
      "daysInWeek": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/WeeklyOpeningHoursRes"
        }
      },
      "daysInYear": {
        "type": "array",
        "items": {
          "$ref": "#/definitions/YearlyOpeningHoursRes"
        }
      },
      "winterSeason": {
        "$ref": "#/definitions/WinterSeasonRes"
      }
    }
  }
}
  • Question. Is this a question about how to do a certain thing? Not sure if this is a bug or feature, couldn’t find any docs on that.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 4
  • Comments: 32 (2 by maintainers)

Commits related to this issue

Most upvoted comments

This is still an issue.

It’s causing a lot of discomfort for us still. Could someone address this issue?

Java version of the Hack by @fzyzcjy (included in a spring Swagger configuration class)

    @Bean
    @Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
    public ModelNamesRegistryFactoryPlugin swaggerFixReqResPostfix() {
        return new DefaultModelNamesRegistryFactory() {
            @Override
            public ModelNamesRegistry modelNamesRegistry(ModelSpecificationRegistry registry) {
                return super.modelNamesRegistry(hackModelSpecificationRegistry(registry));
            }

            private ModelSpecificationRegistry hackModelSpecificationRegistry(ModelSpecificationRegistry delegate) {
                return new ModelSpecificationRegistry() {
                    @Override
                    public ModelSpecification modelSpecificationFor(ModelKey key) {
                        return delegate.modelSpecificationFor(key);
                    }

                    @Override
                    public boolean hasRequestResponsePairs(ModelKey test) {
                        return false;
                    }

                    @Override
                    public Collection<ModelKey> modelsDifferingOnlyInValidationGroups(ModelKey test) {
                        return delegate.modelsDifferingOnlyInValidationGroups(test);
                    }

                    @Override
                    public Collection<ModelKey> modelsWithSameNameAndDifferentNamespace(ModelKey test) {
                        return delegate.modelsWithSameNameAndDifferentNamespace(test);
                    }

                    @Override
                    public Set<ModelKey> modelKeys() {
                        return delegate.modelKeys();
                    }
                };
            }

        };
    }

I am seeing this problem as well after upgrading to 3.0.0. I have not been able to confirm that adding @ApiModelProperty rigorously solves my problem. Instead I have had success replacing all @RequestBody annotations with a corresponding RequestEntity<MyModel> - eg.

This works:

public ResponseEntity<BasketResponse> setOrderContactInfo(
   @ApiParam(value = "Conctact information for the order.", required = true)
   final RequestEntity<OrderContactInfo> contactInfo) {
 }

This does not:

public ResponseEntity<BasketResponse> setOrderContactInfo(
   @ApiParam(value = "Conctact information for the order.", required = true)
   @RequestBody final OrderContactInfo contactInfo) {
 }

Though this can be used as a workaround it is not optimal as it changes the type of the contactInfo parameter and thus requires changes in the method body as well.

@dilipkrish The main issue with the Req/Res approach is that it forces all models to have getters and setters. And this is not a valid assumption as there are other ways of creating beans such the example below using constructors

public class Sorting {
    public Integer index;
    public Direction direction;
    @JsonCreator
    public Sorting(@JsonProperty("index") Integer index, @JsonProperty("direction") Direction direction) {
        this.index = index;
        this.direction = direction;
    }
    public Integer getIndex() {
        return index;
    }
    public Direction getDirection() {
        return direction;
    }
}

Maybe this decision could be based on some configuration, to let each consumer to decide whether create the req/res automatically depending on their API.

Here is another hack without using Regex and a bit shorter:

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER)
class SwaggerFixReqResPostfix(val delegate: DefaultModelNamesRegistryFactory = DefaultModelNamesRegistryFactory()) :
    ModelNamesRegistryFactoryPlugin by delegate {

    override fun modelNamesRegistry(registry: ModelSpecificationRegistry): ModelNamesRegistry =
        delegate.modelNamesRegistry(HackModelSpecificationRegistry(registry))

    class HackModelSpecificationRegistry(delegate: ModelSpecificationRegistry) :
        ModelSpecificationRegistry by delegate {
        // HACK
        override fun hasRequestResponsePairs(test: ModelKey?) = false
    }
}

Label > waiting-for-more-info
What else information do you need on this?

During testing we noticed that adding @ApiModelProperty to every property also fixes the issue.