swagger升级为Knife4j(个性化处理)

/ 后端 / 没有评论 / 658浏览

先说坑,遇到个无法解决的问题

在2.0.6等后面的高版本中,由于升级了Springfox基础组件,如果开发者使用类似JRebel这类热加载插件的时候,会出现类字段没有的情况,目前没有办法解决springfox项目与JRebel插件的冲突,建议是不用JRebel

恰巧我用了JRebel,所以.使用老版本的Knife4j!!!

引入依赖

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>3.0.3</version>
        </dependency>

编写返回实体对象

注意此处的静态方法传入实体对象操作,需要在Rest加上泛型,并且前面也加上泛型,相互对应,使编译期对实体类型检测生效;

public class Rest<T> {
    @ApiModelProperty(value = "返回对象")
    private T data;
    @ApiModelProperty(value = "响应码,正常为0")
    private Integer errCode;
    @ApiModelProperty(value = "响应信息")
    private String msg;

    public static Rest ok() {
        return new Rest();
    }
  
    public static <T> Rest<T> ok(T data) {
        Rest rest = new Rest();
        rest.setData(data);
        return rest;
    }

    public static Rest error() {
        return error(500, "未知异常,请联系管理员");
    }

    public static Rest error(String msg) {
        return error(500, msg);
    }

    public static Rest error(int code, String msg) {
        Rest r = new Rest();
        r.msg = msg;
        r.errCode = code;
        return r;
    }

    public Rest() {
        this.msg = "success";
        this.errCode = 0;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public Integer getErrCode() {
        return errCode;
    }

    public void setErrCode(Integer errCode) {
        this.errCode = errCode;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

编写配置

    @Configuration
    @EnableSwagger2  //开启功能
    @EnableKnife4j   //开启功能
    @Profile("test") //指定环境
    public static class WfDocConfig {
        @Bean
        public Docket createRestApi() {
            return new Docket(DocumentationType.SWAGGER_2) //此处选择版本
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))//筛选注解,也可选择包路径
                    .paths(PathSelectors.any()) //匹配所有
                    .build()
                    .globalRequestParameters(getGlobalRequestParameters())//添加默认参数
                    .globalResponses(HttpMethod.GET, getGlobalResponseMessage())//添加响应状态码
                    .globalResponses(HttpMethod.POST, getGlobalResponseMessage())//添加响应状态码
                    ;
        }
    
        //配置首页介绍
        private ApiInfo apiInfo() {
            return new ApiInfoBuilder()
                    .description("")
                    .contact(new Contact("爱看爱秀技术", "", ""))
                    .version("v1.0")
                    .title("好芳法API")
                    .build();
        }
        
        //配置默认的头信息,调试会自动带上     
        private List<RequestParameter> getGlobalRequestParameters() {
            List<RequestParameter> parameters = new ArrayList<>();
            parameters.add(new RequestParameterBuilder()
                    .name("sign")
                    .description("**签名,调试用含默认值**")
                    .required(true)
                    .in(ParameterType.HEADER)
                    .query(q -> q.model(m -> m.scalarModel(ScalarType.STRING)))
                    .required(false).example(new Example("xxx"))
                    .build());
            return parameters;
        }
        
        //显示自定义状态码      
        private List<Response> getGlobalResponseMessage() {
            List<Response> responseList = new ArrayList<>();
            responseList.add(new ResponseBuilder().code("errCode : 200").description("前端重复提交").build());
            responseList.add(new ResponseBuilder().code("errCode : 0").description("请求成功").build());
            return responseList;
        }
    }

效果

alt alt

注意

有时候配置了拦截器,需要排除swagger的请求地址,如:

        excludePath.add("/doc.html");
        excludePath.add("/swagger-resources");
        excludePath.add("/favicon.ico");
        excludePath.add("/v3/api-docs");
        excludePath.add("/v2/api-docs");
        excludePath.add("/webjars/**");

排除不想展示的接口(比如不想展示DELETE,HEAD等http动词接口)

如果我们的接口使用的@RequestMapping,并且没有指定方法,则默认会展示所有请求方法;当接口很多的时候,返回的数据量很大,响应慢,并且会造成浏览器卡顿;

(1)配置方式:

        @Bean
        public Docket createRestApi2() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("好芳法接口")
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.wfcm.controller"))
                    .paths(PathSelectors.any())
                    .build()
                    .globalResponseMessage(RequestMethod.GET, getGlobalResponseMessage())//添加响应状态码
                    .globalResponseMessage(RequestMethod.POST, getGlobalResponseMessage())//添加响应状态码
                    ;
        }

        @Bean
        public Docket createRestApi2() {
            return new Docket(DocumentationType.SWAGGER_2)
                    .groupName("好芳法接口")
                    .apiInfo(apiInfo())
                    .select()
                    .apis(RequestHandlerSelectors.basePackage("com.wfcm.controller"))
                    .apis(customRequestHandlers())
                    .paths(PathSelectors.any())
                    .build()
                    .globalResponseMessage(RequestMethod.GET, getGlobalResponseMessage())//添加响应状态码
                    .globalResponseMessage(RequestMethod.POST, getGlobalResponseMessage())//添加响应状态码
                    ;
        }

但是这种方式有个缺点,会把没指定方法的@RequestMapping排除;

(2)使用@ControllerAdvice增强器

@Profile("test")
@ControllerAdvice
public class ResponseAdviceController implements ResponseBodyAdvice {

    TimedCache<String, Object> swaggerMap = CacheUtil.newTimedCache(1000 * 10);

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if ("/wfcm_api/v2/api-docs".equals(request.getURI().getPath())) {
            HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest();
            if (body == null) {
                return body;
            }
            JSONObject bodyJson = JSON.parseObject(JSON.toJSONString(body));
            JSONObject paths = bodyJson.getJSONObject("paths");
            if (paths == null) {
                return body;
            }
            Object result = swaggerMap.get(servletRequest.getParameter("group"));
            if (result != null) {
                return result;
            }

            paths.entrySet().parallelStream().forEach(stringObjectEntry -> {
                Object value = stringObjectEntry.getValue();
                if (value == null) {
                    return;
                }
                JSONObject methods = JSON.parseObject(value.toString());
                methods.remove("put");
                methods.remove("delete");
                methods.remove("patch");
                methods.remove("options");
                methods.remove("head");
                stringObjectEntry.setValue(methods);
            });
            String resultJson = bodyJson.toJSONString();
            swaggerMap.put(servletRequest.getParameter("group"), resultJson);
            return resultJson;
        }
        return body;
    }
}

这里我使用了个定时缓存,其实不用也行;由于我使用了jrebel热更新,所以如果不定时过期会出现数据无法更新问题;