J'ai un projet Spring Boot avec springfox-swagger2 2.7.0 et j'ai le contrôleur suivant:

@Api(tags = { "Some" }, description = "CRUD for Some Stuff")
@RestController
@RequestMapping(path = "/some")
public class SomeController {

  @ApiOperation(value = "Get some")
  @GetMapping(value = "{someId}", produces = MediaType.APPLICATION_JSON_VALUE)
  public Response getSomeById(@PathVariable("someId") Id someId) {
    return ...;
  }
...
}

Je veux contrôler ce qui est affiché dans les documents en annotant la classe Id, et cela ne fonctionne que pour certaines parties de l'annotation, mais pas pour toutes. La classe Id (qui a un convertisseur enregistré de String vers Id):

public class Id {

  @ApiParam(value = "This is the description", defaultValue = "1f1f1f",required = true, name = "someId", type = "string")
  private final Long id;

  public Id(Long id) {
    this.id = id;
  }

  public Long getId() {
    return id;
  }
}

Maintenant, le Swagger JSON renvoyé ressemble à ceci:

"parameters":[{
  "name":"id",
  "in":"query",
  "description":"This is the description",
  "required":true,
  "type":"integer",
  "default":"1f1f1f",
  "format":"int64"
}]

Ma question (ou éventuellement un rapport de bogue) est la suivante: pourquoi certaines parties de l'annotation @ApiParam sont-elles utilisées (comme value, defaultValue et required), mais d'autres ne le sont pas, comme name et type? Pourquoi ne semble-t-il pas que je ne puisse pas modifier le name ou le type ici? Pour mon cas d'utilisation particulier, ce dernier est celui que je voudrais changer en string.

Mise à jour

J'ai décidé d'ajouter le composant suivant avec l'aide de skadya.

@Component
public class OverrideSwaggerApiParamBuilder implements 
ExpandedParameterBuilderPlugin {

  @Override
  public boolean supports(DocumentationType type) {
    return DocumentationType.SWAGGER_2 == type;
  }

  @Override
  public void apply(ParameterExpansionContext context) {
    Optional<ApiParam> apiParamOptional = findApiParamAnnotation(context.getField().getRawMember());
    if (apiParamOptional.isPresent()) {
      ApiParam param = apiParamOptional.get();
      context.getParameterBuilder()
          .name(param.name())
          .modelRef(new ModelRef(param.type()))
          .build();
    }
  }
}

Les auteurs de springfox pensent que cela pourrait être un bogue: https://github.com/springfox/springfox / issues / 2107

12
Michiel Haisma 16 nov. 2017 à 00:29

4 réponses

Meilleure réponse

Par défaut, les attributs @ApiParam 'nom' et 'type' sont utilisés pour remplacer le nom du paramètre et le type détecté des paramètres directs spécifiés dans la méthode API. Lorsque vous utilisez @ApiParam sur un champ, le type et le nom sont déduits par le nom du champ et son type déclaré et la valeur de remplacement pour le nom et le type ne sont pas pris en compte. (Cela semble par conception dans springfox, vous pouvez jeter un œil à l'implémentation springfox.documentation.swagger.readers.parameter.SwaggerExpandedParameterBuilder)

Si vous souhaitez toujours modifier ce comportement, vous pouvez enregistrer une implémentation personnalisée de springfox.documentation.spi.service.ExpandedParameterBuilderPlugin entrelacé.

Par exemple

@Component
public class OverrideSwaggerApiParamNameBuilder implements ExpandedParameterBuilderPlugin {

    @Override
    public boolean supports(DocumentationType type) {
        return DocumentationType.SWAGGER_2 == type;
    }

    @Override
    public void apply(ParameterExpansionContext context) {
        Optional<ApiParam> apiParamOptional = findApiParamAnnotation(context.getField().getRawMember());
        if (apiParamOptional.isPresent()) {
            fromApiParam(context, apiParamOptional.get());
        }
    }

    private void fromApiParam(ParameterExpansionContext context, ApiParam apiParam) {
        context.getParameterBuilder()
                .name(emptyToNull(apiParam.name()));
    }

    private String emptyToNull(String str) {
        return StringUtils.hasText(str) ? str : null;
    }
}

J'espère que cela aide.

6
skadya 21 nov. 2017 à 22:59

Idéalement, vous devez utiliser @ApiParam avec les paramètres de méthode tandis que @ApiModelProperty avec les propriétés du modèle.

public @interface ApiParam {
    /**
     * The parameter name.
     * The name of the parameter will be derived from the field/method/parameter name,
     * however you can override it.
     * Path parameters must always be named as the path section they represent.
     */
    String name() default "";

Je ne sais pas si l'attribut type est présent, mais voici la façon de traiter les types:

public @interface ApiModelProperty {

    /**
     * The data type of the parameter.
     * This can be the class name or a primitive. The value will override the data type as read from the class
     * property.
     */
    String dataType() default "";

......
-1
productioncoder 20 nov. 2017 à 17:21

J'utilise la version 2.6.1 et je n'arrive pas à trouver l'attribut "type" dans @ApiParam alors que je peux voir que vous utilisez "type" avec cela. Assurez-vous donc qu'il est disponible. J'ai également mentionné que @ApiModelProperty fournit dataType () pour gérer le scénario que vous avez mentionné.

-1
Har Krishan 21 nov. 2017 à 08:22

Une solution plus complète qui compile réellement et prend en compte le paramètre du type à la fois de la propriété de type ApiParam ou de la propriété Model dataType:

@Component
@Order(SwaggerPluginSupport.SWAGGER_PLUGIN_ORDER + 1000)
public class OverrideSwaggerApiParamTypeBuilder extends 
SwaggerExpandedParameterBuilder implements ExpandedParameterBuilderPlugin {

public OverrideSwaggerApiParamTypeBuilder(DescriptionResolver descriptions, EnumTypeDeterminer enumTypeDeterminer) {
    super(descriptions, enumTypeDeterminer);
}

@Override
public boolean supports(DocumentationType type) {
    return DocumentationType.SWAGGER_2 == type;
}

public void apply(ParameterExpansionContext context) {
    super.apply(context);
    Optional<ApiModelProperty> apiModelPropertyOptional = context.findAnnotation(ApiModelProperty.class);
    if (apiModelPropertyOptional.isPresent()) {
        if(!StringUtils.isAllEmpty(apiModelPropertyOptional.get().dataType())) {
            context.getParameterBuilder().modelRef(new ModelRef(apiModelPropertyOptional.get().dataType()));
        }
    }

    Optional<ApiParam> apiParamOptional = context.findAnnotation(ApiParam.class);
    if (apiParamOptional.isPresent()) {
        if(!StringUtils.isAllEmpty(apiParamOptional.get().type())) {
            context.getParameterBuilder().modelRef(new ModelRef(apiParamOptional.get().type()));
        }
    }

}

}

0
aciobanu 12 sept. 2019 à 08:11
47317562