springboot全局响应类与swagger冲突
近日,在学习springboot全局异常处理与全局响应对象时,因为项目本身使用了swagger,启用全局响应处理类后,swagger页面无法使用,如下图
全局响应类需集成ResponseBodyAdvice接口,并添加注解@ControllerAdvice和@Component,交由spring进行管理
经排查后,是由于全局响应类中supports方法拦截了请求,在supports方法的返回值前增加过滤判断,是swagger的Docket类则返回false
改动后代码如下:
1 package com.myfather.demo3.exception; 2 3 import com.myfather.demo3.common.AjaxResponse; 4 import org.springframework.core.MethodParameter; 5 import org.springframework.http.HttpStatus; 6 import org.springframework.http.MediaType; 7 import org.springframework.http.server.ServerHttpRequest; 8 import org.springframework.http.server.ServerHttpResponse; 9 import org.springframework.stereotype.Component; 10 import org.springframework.web.bind.annotation.ControllerAdvice; 11 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; 12 import springfox.documentation.spring.web.plugins.Docket; 13 14 /** 15 * @Description 全局响应类 16 * @Author liangZai 17 * @Date 2020/12/17 22:09 18 * @Version 1.0 19 */ 20 @Component 21 @ControllerAdvice 22 public class GlobalResponseAdvice implements ResponseBodyAdvice { 23 /** 24 * Whether this component supports the given controller method return type 25 * and the selected {@code HttpMessageConverter} type. 26 * 27 * @param returnType the return type 28 * @param converterType the selected converter type 29 * @return {@code true} if {@link #beforeBodyWrite} should be invoked; 30 * {@code false} otherwise 31 */ 32 @Override 33 public boolean supports(MethodParameter returnType, Class converterType) { 34 //默认结果为false,表示支持哪些方法,改为true之后为支持所有方法 35 // return false; 36 return !returnType.getDeclaringClass().equals(Docket.class); 37 // return true; 38 } 39 40 /** 41 * Invoked after an {@code HttpMessageConverter} is selected and just before 42 * its write method is invoked. 43 * 44 * @param body the body to be written 45 * @param returnType the return type of the controller method 46 * @param mediaType the content type selected through content negotiation 47 * @param selectedConverterType the converter type selected to write to the response 48 * @param request the current request 49 * @param response the current response 50 * @return the body that was passed in or a modified (possibly new) instance 51 */ 52 @Override 53 public Object beforeBodyWrite(Object body, 54 MethodParameter returnType, 55 MediaType mediaType, 56 Class selectedConverterType, 57 ServerHttpRequest request, 58 ServerHttpResponse response) { 59 60 if(mediaType.equalsTypeAndSubtype(MediaType.APPLICATION_JSON)){ 61 if(body instanceof AjaxResponse){ 62 response.setStatusCode(HttpStatus.valueOf(( 63 (AjaxResponse)body).getCode() 64 )); 65 return body; 66 } 67 else { 68 response.setStatusCode(HttpStatus.OK); 69 return AjaxResponse.success(body); 70 } 71 } 72 return null; 73 } 74 }
改动前
代码改动后
以下贴出代码
统一响应类
package com.myfather.demo3.common; import com.myfather.demo3.exception.CustomException; import com.myfather.demo3.exception.CustomExceptionType; import lombok.Data; /** * @Description * @Author liangZai * @Date 2020/12/17 21:49 * @Version 1.0 */ @Data public class AjaxResponse { private boolean isok; private int code; private String message; private Object data; private AjaxResponse() { } /** * 请求成功参数封装 * @return */ public static AjaxResponse success(){ AjaxResponse response = new AjaxResponse(); response.setIsok(true); response.setCode(200); response.setMessage("请求成功!"); return response; } /** * 请求成功带参 * @param obj * @return */ public static AjaxResponse success(Object obj){ AjaxResponse response = new AjaxResponse(); response.setIsok(true); response.setCode(200); response.setMessage("请求成功!"); response.setData(obj); return response; } /** * 请求成功带参且自定义消息 * @param obj * @param message * @return */ public static AjaxResponse success(Object obj,String message){ AjaxResponse response = new AjaxResponse(); response.setIsok(true); response.setCode(200); response.setMessage(message); response.setData(obj); return response; } /** * 请求异常参数封装 * @param e * @return */ public static AjaxResponse error(CustomException e){ AjaxResponse response = new AjaxResponse(); response.setIsok(false); response.setCode(e.getCode()); response.setMessage(e.getMessage()); return response; } /** * 请求异常带参数且自定义消息 * @param customExceptionType * @param message * @return */ public static AjaxResponse error(CustomExceptionType customExceptionType,String message){ AjaxResponse response = new AjaxResponse(); response.setIsok(false); response.setCode(customExceptionType.getCode()); response.setMessage(message); return response; } }
自定义异常类
package com.myfather.demo3.exception; import lombok.Data; /** * @Description * @Author liangZai * @Date 2020/12/17 21:44 * @Version 1.0 */ @Data public class CustomException extends RuntimeException { private int code; private String message; private CustomException() { } public CustomException(CustomExceptionType customExceptionType,String message) { this.code = customExceptionType.getCode(); this.message = message; } public CustomException(CustomExceptionType customExceptionType) { this.code = customExceptionType.getCode(); this.message = customExceptionType.getDesc(); } }
自定义异常code枚举类
package com.myfather.demo3.exception; /** * 异常类型 */ public enum CustomExceptionType { USER_INPUT_ERROR(400,"用户输入参数有误!"), SYSTEM_ERROR(500,"服务器异常!"), OTHER_ERROR(999,"系统出现未知异常,请联系管理员!"), ; private int code; private String desc; CustomExceptionType(int code, String desc) { this.code = code; this.desc = desc; } public int getCode() { return code; } public String getDesc() { return desc; } }
全局异常处理类
package com.myfather.demo3.exception; import com.myfather.demo3.common.AjaxResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; /** * @Description * @Author liangZai * @Date 2020/12/17 22:02 * @Version 1.0 */ @ControllerAdvice public class WebExceptionHandler { @ExceptionHandler(CustomException.class) @ResponseBody public AjaxResponse customerException(CustomException e){ return AjaxResponse.error(e); } @ExceptionHandler(Exception.class) @ResponseBody public AjaxResponse customerException(Exception e){ return AjaxResponse.error(new CustomException(CustomExceptionType.OTHER_ERROR)); } }
swagger配置类
package com.myfather.demo3; import io.swagger.annotations.Api; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import springfox.documentation.builders.ApiInfoBuilder; import springfox.documentation.builders.PathSelectors; import springfox.documentation.builders.RequestHandlerSelectors; import springfox.documentation.service.ApiInfo; import springfox.documentation.spi.DocumentationType; import springfox.documentation.spring.web.plugins.Docket; import springfox.documentation.swagger2.annotations.EnableSwagger2; /** * swagger配置文件 * * @author liangZai */ @Configuration @EnableSwagger2 @ComponentScan(basePackages = {"com.myfather.demo3"}) @ConditionalOnProperty(prefix = "myconfig", name = "swagger-ui-open", havingValue = "true") public class SwaggerConfig { @Bean public Docket createAccepterRestApi() { return new Docket(DocumentationType.SWAGGER_2) //分组名,不指定默认为default .groupName("我的API") .select() .apis(RequestHandlerSelectors.withClassAnnotation(Api.class)) // 定义要生成文档的Api的url路径规则 .paths(PathSelectors.any()) .build() // 设置swagger-ui.html页面上的一些元素信息 .apiInfo(apiInfo()) .enable(true); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("我的RESTful API") .description("提供各种牛掰功能") .version("1.0") .build(); } }