swagger 動態顯示枚舉內容 + 數值類型空指針異常統一控制

遇到的問題:

  1. 當咱們定義接口的入參或者返回值的字段想把他關聯的枚舉內容顯示到swagger的頁面上方便前端同窗查看,可是又不想在每次修改枚舉值的時候去手動修改description,若是不去手動修改又會遇到先後端掌握的枚舉值不一致的狀況
  2. 定義的接口參數Integer、Boolean 字段的時候,若是不去設置該值的example的時候,獲取swagger頁面的時候,會提示nullpointException

解決方法:

問題1:攔截生成swagger doc的過程,在這個過程判斷字段是不是枚舉字段,遍歷完後設置到description中
問題2:攔截生成swagger doc過程,若是是數值字段或者Boolean,設置默認的example前端

show 代碼

  • 定義攔截方法,對數值和枚舉作替換
import com.fasterxml.classmate.ResolvedType;
import com.google.common.base.Optional;
import io.swagger.annotations.ApiModelProperty;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Primary;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import springfox.documentation.builders.ModelPropertyBuilder;
import springfox.documentation.schema.Annotations;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.schema.ModelPropertyBuilderPlugin;
import springfox.documentation.spi.schema.contexts.ModelPropertyContext;
import springfox.documentation.swagger.schema.ApiModelProperties;

/**
 * 對swagger展現進行修改 1. 數字、Boolean屬性設置默認的example 2. 支持將枚舉變量的描述按照枚舉類定義展現
 *
 * @see SwaggerDisplayEnum
 * @see ApiModelProperty
 */
@Component
@Primary
@Slf4j
public class SwaggerDisplayConfig implements ModelPropertyBuilderPlugin {

    /**
     * 是否容許swagger
     */
    @Value("${swagger.enable:false}")
    private Boolean enableSwagger;

    @Override
    public void apply(ModelPropertyContext context) {
        //若是不支持swagger的話,直接返回
        if (!enableSwagger) {
            return;
        }

        //獲取當前字段的類型
        final Class fieldType = context.getBeanPropertyDefinition().get().getField().getRawType();

        //設置對數字的字段設置默認的example
        setDefaultExample(context, fieldType);

        //爲枚舉字段設置註釋
        descForEnumFields(context, fieldType);
    }

    /**
     * 爲枚舉字段設置註釋
     */
    private void descForEnumFields(ModelPropertyContext context, Class fieldType) {
        Optional<ApiModelProperty> annotation = Optional.absent();

        if (context.getAnnotatedElement().isPresent()) {
            annotation = annotation
                .or(ApiModelProperties.findApiModePropertyAnnotation(context.getAnnotatedElement().get()));
        }
        if (context.getBeanPropertyDefinition().isPresent()) {
            annotation = annotation.or(Annotations.findPropertyAnnotation(
                context.getBeanPropertyDefinition().get(),
                ApiModelProperty.class));
        }

        //沒有@ApiModelProperty 或者 notes 屬性沒有值,直接返回
        if (!annotation.isPresent() || StringUtils.isBlank((annotation.get()).notes())) {
            return;
        }

        //@ApiModelProperties中的notes指定的class類型
        Class rawPrimaryType;
        try {
            rawPrimaryType = Class.forName((annotation.get()).notes());
        } catch (ClassNotFoundException e) {
            //若是指定的類型沒法轉化,直接忽略
            return;
        }

        //若是對應的class是一個@SwaggerDisplayEnum修飾的枚舉類,獲取其中的枚舉值
        Object[] subItemRecords = null;
        SwaggerDisplayEnum swaggerDisplayEnum = AnnotationUtils
            .findAnnotation(rawPrimaryType, SwaggerDisplayEnum.class);
        if (null != swaggerDisplayEnum && Enum.class.isAssignableFrom(rawPrimaryType)) {
            subItemRecords = rawPrimaryType.getEnumConstants();
        }
        if (null == subItemRecords) {
            return;
        }

        //從annotation中獲取對應的值和描述的變量名
        String valueName = swaggerDisplayEnum.valueName();
        String descName = swaggerDisplayEnum.descName();
        if (StringUtils.isBlank(valueName) || StringUtils.isBlank(descName)) {
            return;
        }

        final List<String> displayValues = Arrays.stream(subItemRecords).filter(Objects::nonNull).map(item -> {
            Class currentClass = item.getClass();

            String value;
            String desc;
            try {
                Field valueField = currentClass.getField(valueName);
                Field descField = currentClass.getField(descName);
                valueField.setAccessible(true);
                descField.setAccessible(true);
                value = valueField.get(item).toString();
                desc = descField.get(item).toString();
            } catch (NoSuchFieldException | IllegalAccessException e) {
                log.warn("獲取枚舉的屬性和值失敗, {}", e.getMessage());
                return null;
            }
            return value + ":" + desc;
        }).filter(Objects::nonNull).collect(Collectors.toList());

        String joinText = " (" + String.join("; ", displayValues) + ")";
        try {
            Field mField = ModelPropertyBuilder.class.getDeclaredField("description");
            mField.setAccessible(true);
            joinText = mField.get(context.getBuilder()) + joinText;
        } catch (Exception e) {
            log.error(e.getMessage());
        }

        final ResolvedType resolvedType = context.getResolver().resolve(fieldType);
        context.getBuilder().description(joinText).type(resolvedType);
    }

    /**
     * 設置默認的example
     */
    private void setDefaultExample(ModelPropertyContext context, Class fieldType) {
        if (Number.class.isAssignableFrom(fieldType)) {
            context.getBuilder().example("1");
        }
        if (Boolean.class.isAssignableFrom(fieldType)) {
            context.getBuilder().example("true");
        }
    }

    @Override
    public boolean supports(DocumentationType documentationType) {
        return true;
    }
}

複製代碼
  • 定義用來判斷是否須要顯示枚舉到swagger上的annotation
/**
 * 須要swagger展現的枚舉
 * 默認 支持 枚舉中屬性名爲 value和desc
 * 若是不是,請設置valueName和descName
 *
 */
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface SwaggerDisplayEnum {

    String valueName() default "value";
    String descName() default "desc";

}
複製代碼
  • 在定義出入參bean的時候,在想要顯示枚舉詳情的字段上@ApiModelProperties屬性中指定該字段對應的枚舉類
@Data
@ApiModel("返回值bean")
public class ResultBean {

....
/**
 * 枚舉字段
 */
@ApiModelProperty(value = "須要顯示的枚舉值", notes = "com.li.test.enums.AAEnum")
private Integer aa;
...

}

複製代碼

完成上面的操做,aa對應的枚舉類AAEnum的值和意思都能在swagger字段的description中顯示出來了。java

相關文章
相關標籤/搜索