【问题标题】:Generate Swagger from JAX-RS endpoint with external enum definition使用外部枚举定义从 JAX-RS 端点生成 Swagger
【发布时间】:2017-06-28 17:11:47
【问题描述】:

我想从具有外部枚举定义的 JAX-RS 端点生成一个 swagger,但是生成的 swagger 直接将枚举包含到模型的定义中。这意味着没有生成枚举文档,但也意味着在客户端重复了相同的枚举。

我使用swagger-jaxrs 依赖项扫描我的端点并生成swagger json 文件。此 GitHub repository 可用于重现该问题。我还在 swagger-core 存储库上创建了一个 GitHub issue

JAX-RS 端点

@Api("hello")
@Path("/helloSwagger")
public class HelloSwagger {

    @ApiOperation(value = "Get all unique customers", notes = "Get all customers matching the given search string.", responseContainer = "Set", response = User.class)
    @GET
    @Path("/getUniqueUsers")
    @Produces(MediaType.APPLICATION_JSON)
    public Set<User> getUniqueUsers(
            @ApiParam(value = "The search string is used to find customer by their name. Not case sensitive.") @QueryParam("search") String searchString,
            @ApiParam(value = "Limits the size of the result set", defaultValue = "50") @QueryParam("limit") int limit
    ) {
        return new HashSet<>(Arrays.asList(new User(), new User()));
    }

}

带有枚举的模型

public class User {

    private String name = "unknown";
    private SynchronizationStatus ldap1 = SynchronizationStatus.UNKNOWN;
    private SynchronizationStatus ldap2 = SynchronizationStatus.OFFLINE;

    @ApiModelProperty(value = "The user name")
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @ApiModelProperty(value = "The synchronization status with the LDAP1")
    public SynchronizationStatus getLdap1() {
        return ldap1;
    }

    public void setLdap1(SynchronizationStatus ldap1) {
        this.ldap1 = ldap1;
    }

    public SynchronizationStatus getLdap2() {
        return ldap2;
    }

    public void setLdap2(SynchronizationStatus ldap2) {
        this.ldap2 = ldap2;
    }
}

@ApiModel("The synchronization status with LDAP instance.")
public enum SynchronizationStatus {

    UNKNOWN,
    SYNC,
    OFFLINE,
    CONFLICT
}

大摇大摆的摘录

{
  (...)
  },
  "definitions" : {
    "User" : {
      "type" : "object",
      "properties" : {
        "name" : {
          "type" : "string",
          "description" : "The user name"
        },
        "ldap1" : {
          "type" : "string",
          "description" : "The synchronization status with the LDAP1",
          "enum" : [ "UNKNOWN", "SYNC", "OFFLINE", "CONFLICT" ]
        },
        "ldap2" : {
          "type" : "string",
          "enum" : [ "UNKNOWN", "SYNC", "OFFLINE", "CONFLICT" ]
        }
      }
    }
  }
}

预期结果

{
  (...)
  "definitions" : {
    "SynchronizationStatus" : {
      "description" : "The synchronization status with LDAP instance.",
      "enum" : [ "UNKNOWN", "SYNC", "OFFLINE", "CONFLICT" ],
      "type" : "string"
    },
    "User" : {
      "type" : "object",
      "properties" : {
        "name" : {
          "type" : "string",
          "description" : "The user name"
        },
        "ldap1" : {
          "$ref" : "#/definitions/SynchronizationStatus"
        },
        "ldap2" : {
          "$ref" : "#/definitions/SynchronizationStatus"
        }
      }
    }
  }
}

我做错了什么还是swagger-jaxrs 库的“功能”?

感谢您的帮助

【问题讨论】:

  • 你试过@ApiModelProperty注解的reference属性吗?
  • @CássioMazzochiMolin 它也不起作用(见我的comment)。

标签: java rest enums jax-rs swagger


【解决方案1】:

我做错了什么还是 swagger-jaxrs 的“功能” 图书馆?

枚举值被 swagger 视为原始值类型,并且 swagger 开箱即用不会生成枚举类型的模型定义(see code 第 209 行下)。所以这是一个特性,与 swagger-jaxrs 无关。

但是,您可以通过提供自定义模型转换器 (io.swagger.converter.ModelConverter),按照您的期望生成 swagger 定义。

但在我看来,开箱即用的 swagger 是一个不错的功能。

以下是一个 ruff 实现,它可以帮助您生成预期的 swagger 定义。

package nhenneaux.test.swagger.ext;

import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Iterator;
import java.util.List;

import com.fasterxml.jackson.databind.JavaType;

import io.swagger.annotations.ApiModel;
import io.swagger.converter.ModelConverter;
import io.swagger.converter.ModelConverterContext;
import io.swagger.jackson.ModelResolver;
import io.swagger.models.Model;
import io.swagger.models.ModelImpl;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.StringProperty;
import io.swagger.util.Json;

public class EnumAsModelAwareResolver extends ModelResolver {
    static final EnumAsModelAwareResolver INSTANCE = new EnumAsModelAwareResolver();

    public EnumAsModelAwareResolver() {
        super(Json.mapper());
    }

    @Override
    public Property resolveProperty(Type type, ModelConverterContext context, Annotation[] annotations,
            Iterator<ModelConverter> chain) {
        if (isEnumAnApiModel(type)) {
            String name = findName(type);
            // ask context to resolver enum type (for adding model definition
            // for enum under definitions section
            context.resolve(type);

            return new RefProperty(name);
        }
        return chain.next().resolveProperty(type, context, annotations, chain);
    }

    private String findName(Type type) {
        JavaType javaType = _mapper.constructType(type);
        Class<?> rawClass = javaType.getRawClass();
        ApiModel annotation = rawClass.getAnnotation(ApiModel.class);
        String name = annotation.value();
        if (name == null || name.length() == 0) {
            name = rawClass.getSimpleName();
        }
        return name;
    }

    private boolean isEnumAnApiModel(Type type) {
        JavaType javaType = _mapper.constructType(type);
        return javaType.isEnumType()
                && javaType.getRawClass().isAnnotationPresent(ApiModel.class);
    }

    @Override
    public Model resolve(Type type, ModelConverterContext context, Iterator<ModelConverter> chain) {
        JavaType javaType = Json.mapper().constructType(type);
        if (javaType.isEnumType()) {
            ModelImpl model = new ModelImpl();
            Class<?> rawClass = javaType.getRawClass();
            ApiModel annotation = rawClass.getAnnotation(ApiModel.class);
            String name = annotation.value();
            if (name == null || name.length() == 0) {
                name = rawClass.getSimpleName();
            }
            model.setName(name);
            model.setDescription(annotation.description());
            model.setType(StringProperty.TYPE);

            List<String> constants = findEnumConstants(rawClass);
            model.setEnum(constants);
            return model;
        }
        return chain.next().resolve(type, context, chain);
    }

    private List<String> findEnumConstants(Class<?> rawClass) {
        StringProperty p = new StringProperty();
        _addEnumProps(rawClass, p);
        return p.getEnum();
    }

}

package nhenneaux.test.swagger.ext;

import io.swagger.converter.ModelConverters;
import io.swagger.jaxrs.config.BeanConfig;
import nhenneaux.test.swagger.ext.EnumAsModelAwareResolver;

public class EnumModelAwareBeanConfig extends BeanConfig {
    public EnumModelAwareBeanConfig() {
        registerResolver();
    }

    private void registerResolver() {
        ModelConverters modelConverters = ModelConverters.getInstance();
        // remove and add; in case it is called multiple times.
        // should find a better way to register this.
        modelConverters.removeConverter(EnumAsModelAwareResolver.INSTANCE);
        modelConverters.addConverter(EnumAsModelAwareResolver.INSTANCE);
    }

}

在您的测试中使用:

final BeanConfig beanConfig = new nhenneaux.test.endpoint.model.EnumModelAwareBeanConfig();
    

这有帮助。

【讨论】:

  • 我已经推送了 branch 与您提出的更改但是,定义和枚举之间的引用使用定义的描述。测试仍然失败,但我认为这是解决问题的好方法。
  • 使用的描述而不是参考存在问题,但我已在 GitHub 的 branch 中修复它。
【解决方案2】:

你可以试试@ApiModelProperty注解的reference属性:

@ApiModelProperty(reference = "#/definitions/SynchronizationStatus")
public SynchronizationStatus getLdap1() {
    return ldap1;
}

【讨论】:

  • 它生成引用但不生成外部枚举。因此,生成的招摇是无效的。我根据您的建议在 GitHub 存储库上创建了branch
  • @NicolasHenneaux 我明白了。至少对我来说,即使没有我建议的修改,它也会发生。
  • 你用的是什么插件?您可以检查存储库与您的配置有什么区别
  • @NicolasHenneaux 我使用 IntelliJ IDEA 2016.3 克隆了您的存储库,然后运行 ​​HelloSwaggerTest 测试。 SynchronizationStatus对象不是在definitions对象下生成的。
  • 您能分享一下您在comment 中使用的配置吗?
【解决方案3】:

基于去年的mailing list post,我相信这不是微不足道的,可能需要扩展适当的 Swagger 资源。唯一的其他选择是按照Cássio Mazzochi Molin's answer 手动引用模型(请注意,重命名 SynchronizationStatus 不会因为强制使用未生成的字符串而破坏 API 文档)

【讨论】:

    【解决方案4】:

    我能够通过(使用 swagger-jaxrs-2.1.3)实现这一目标

    System.setProperty(ModelResolver.SET_PROPERTY_OF_ENUMS_AS_REF, "true");
    Reader reader = new Reader();
    OpenAPI api = reader.read(...);
    

    【讨论】:

      猜你喜欢
      • 2016-03-28
      • 2023-02-07
      • 1970-01-01
      • 1970-01-01
      • 2015-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多