添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

1、每个方法的返回都是Result封装对象,没有业务含义 2、在业务代码中,成功的时候我们调用Result.success,异常错误调用Result.failure。是不是很多余 3、上面的代码,判断id是否为null,其实我们可以使用validate做校验,没有必要在方法体中做判断。

1、定义一个注解@ResponseResult,表示这个接口返回的值需要包装一下 2、拦截请求,判断此请求是否需要被@ResponseResult注解 3、核心步骤就是实现接口ResponseBodyAdvice和@ControllerAdvice,判断是否需要包装返回值,如果需要,就把Controller接口的返回值进行重写。

新建Maven项目

1、pom.xml文件

<groupId>com.fang</groupId>
<artifactId>result-response</artifactId>
<version>1.0</version>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.1.7.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>RELEASE</version>
    </dependency>
</dependencies>

2、定义返回状态码,这里实现ResultCode接口,为了可以扩展ResultCodeEnum枚举类,自定义返回状态码。

public interface ResultCode {
    Integer getCode();
    String getMessage();
    ResultCode setMessage(String message);
  
  • setMessage方法用来自定义返回信息
  • public enum ResultCodeEnum implements ResultCode {
         * 成功状态码
        SUCCESS(0, "成功"),
        /* 参数错误:1001-1999 */
        PARAM_IS_INVALID(1001, "参数无效"),
        PARAM_IS_BLANK(1002, "参数为空"),
        PARAM_TYPE_ERROR(1003, "参数类型错误"),
        PARAM_NOT_COMPLETE(1004, "参数缺失"),
        /* 用户错误 */
        USER_NOT_LOGIN(2001, "用户未登录"),
        USER_LOGIN_ERROR(2002, "账号或密码错误"),
        USER_ACCOUNT_FORBIDDEN(2003, "账号被禁用"),
        USER_NOT_EXIST(2004, "用户不存在"),
        USER_HAS_EXIST(2005, "用户已存在"),
        /* 请求错误 */
        METHOD_REQUEST_ERROR(3001, "请求方法错误"),
        NOT_FOUND(3002, "请求地址不存在"),
        /* 未知错误 */
        UNKNOWN_ERROR(5000, "未知错误")
        private Integer code;
        private String message;
        ResultCodeEnum(Integer code, String message) {
            this.code = code;
            this.message = message;
        @Override
        public Integer getCode() {
            return code;
        @Override
        public String getMessage() {
            return message;
        @Override
        public ResultCode setMessage(String message) {
            this.message = message;
            return this;
    

    3、定义返回类

    @Data
    public class Result<T> implements Serializable {
        private Integer code;
        private String message;
        private T data;
        public Result(ResultCode resultCode, T data) {
            this.code = resultCode.getCode();
            this.message = resultCode.getMessage();
            this.data = data;
         * 返回成功
         * @param <T>
         * @return
        public static Result success() {
            return new Result(ResultCodeEnum.SUCCESS, null);
         * 返回成功,带数据
         * @param data
         * @param <T>
         * @return
        public static <T> Result success(T data) {
            return new Result(ResultCodeEnum.SUCCESS, data);
         * 返回失败
         * @param resultCode
         * @return
        public static Result fail(ResultCode resultCode) {
            return new Result(resultCode, null);
    

    4、定义业务异常类,包装状态码接口

    public class BusinessException extends RuntimeException {
        private ResultCode resultCode;
         * Constructs a new runtime exception with {@code null} as its
         * detail message.  The cause is not initialized, and may subsequently be
         * initialized by a call to {@link #initCause}.
        public BusinessException(ResultCode resultCode) {
            this.resultCode = resultCode;
        public ResultCode getResultCode() {
            return resultCode;
    

    5、定义全局异常处理类

    @Slf4j
    @RestControllerAdvice
    public class ExceptionController {
        @ExceptionHandler(BusinessException.class)
        public ResultCode handleException(BusinessException ex) {
            log.info(ex.getMessage());
            return ex.getResultCode();
        @ExceptionHandler(MethodArgumentNotValidException.class)
        public ResultCode parameterExceptionHandler(MethodArgumentNotValidException e) {
            log.info(e.getMessage());
            // 获取异常信息
            BindingResult exceptions = e.getBindingResult();
            // 判断异常中是否有错误信息,如果存在就使用异常中的消息,否则使用默认消息
            if (exceptions.hasErrors()) {
                List<ObjectError> errors = exceptions.getAllErrors();
                if (!errors.isEmpty()) {
                    // 这里列出了全部错误参数,按正常逻辑,只需要第一条错误即可
                    FieldError fieldError = (FieldError) errors.get(0);
                    return ResultCodeEnum.PARAM_IS_INVALID.setMessage(fieldError.getDefaultMessage());
            return ResultCodeEnum.PARAM_IS_INVALID;
        @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
        public Result methodNotSupportedExceptionHandler(HttpRequestMethodNotSupportedException ex) {
            log.info(ex.getMessage());
            return Result.fail(ResultCodeEnum.METHOD_REQUEST_ERROR);
        @ExceptionHandler(NoHandlerFoundException.class)
        public Result noHandlerFoundException(NoHandlerFoundException ex) {
            log.info(ex.getMessage());
            return Result.fail(ResultCodeEnum.NOT_FOUND);
        @ExceptionHandler(Exception.class)
        public ResultCode otherException(Exception ex) {
            log.info(ex.getMessage());
            return ResultCodeEnum.UNKNOWN_ERROR.setMessage(ex.getMessage());
      
  • 注意:HttpRequestMethodNotSupportedExceptionNoHandlerFoundException是直接返回结果,其它接口是返回状态码,因为它们没有到Controller层,@ControllerAdvice拦截不了,所以直接返回。
  • parameterExceptionHandler是处理@Valid校验异常
  • 6、自定义注解

    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ResponseResult {
    

    7、定义拦截器,判断是否有ResponseResult注解,是否需要返回值包装,设置一个属性标记。

    @Slf4j
    @Component
    public class ResponseResultInterceptor implements HandlerInterceptor {
        public static final String RESPONSE_RESULT = "RESPONSE_RESULT";
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            System.out.println("request = " + request.getServerName());
            log.info(this.toString());
            if (handler instanceof HandlerMethod) {
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                Class<?> beanType = handlerMethod.getBeanType();
                Method method = handlerMethod.getMethod();
                if (beanType.isAnnotationPresent(ResponseResult.class)) {
                    request.setAttribute(RESPONSE_RESULT, beanType.getAnnotation(ResponseResult.class));
                } else if (method.isAnnotationPresent(ResponseResult.class)) {
                    request.setAttribute(RESPONSE_RESULT, method.getAnnotation(ResponseResult.class));
                log.info("Attribute: " + request.getAttribute(RESPONSE_RESULT));
            return true;
    

    8、返回接口重写处理

    @Slf4j
    @RestControllerAdvice
    public class ResponseResultHandler implements ResponseBodyAdvice<Object> {
        @Override
        public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            ResponseResult attribute = (ResponseResult) request.getAttribute(ResponseResultInterceptor.RESPONSE_RESULT);
            if (attribute != null) {
                return true;
            return false;
        @Nullable
        @Override
        public Object beforeBodyWrite(@Nullable Object t, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
            log.info("进入返回体重写处理中: " + t.toString());
            if (t instanceof ResultCode) {
                ResultCode resultCode = (ResultCode) t;
                return Result.fail(resultCode);
            if (t instanceof Result) {
                return t;
            return Result.success(t);
    

    9、打包成starter项目

    spring.factories文件:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.fang.config.ResponseConfig
    

    定义Java配置类:

    @Configuration
    @ComponentScan
    public class ResponseConfig {
        @Bean
        public ResponseResultHandler responseResultHandler() {
            return new ResponseResultHandler();
        @Bean
        public ExceptionController exceptionController() {
            return new ExceptionController();
      
  • 注意:要能注入到Spring IOC容器里需要加上@ComponentScan注解
  • 注入拦截器:

    @Configuration
    public class MyConfigurerAdapter extends WebMvcConfigurationSupport {
        @Bean
        public ResponseResultInterceptor responseResultInterceptor() {
            return new ResponseResultInterceptor();
        @Override
        protected void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(responseResultInterceptor());
        @Override
        protected void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            super.configureMessageConverters(converters);
            converters.add(0, new MappingJackson2HttpMessageConverter());
      
  • configureMessageConverters是为了解决该项目引入到其它项目时出现的类型转换问题com.fang.response.Result cannot be cast to java.lang.String,原因是因为在所有的 HttpMessageConverter 实例集合中,StringHttpMessageConverter 要比其它的 Converter 排得靠前一些。我们需要将处理 Object 类型的 HttpMessageConverter 放得靠前一些,所以当返回String类型是就会报错。其它类型没问题。
  • 最后使用mvn install打包。

    1、创建Spring Boot项目,引入依赖:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.fang</groupId>
        <artifactId>result-response</artifactId>
        <version>1.0</version>
    </dependency>
    

    2、在启动类添加注解,使拦截器生效。

    @SpringBootApplication
    @Import({MyConfigurerAdapter.class})
    public class ReponseUsedemoApplication {
    	public static void main(String[] args) {
    		ConfigurableApplicationContext context = SpringApplication.run(ReponseUsedemoApplication.class, args);
    

    3、其它的跟以前没有区别,只是在Controller加上@ResponseResult即可,该返回什么类型就返回什么类型。@ResponseResult注解也可以加在方法上。

    @ResponseResult @RestController @RequestMapping("order") public class OrderController { @ApiOperation("订单详情") @PostMapping("detail") public List<OrderVo> getOrder(@RequestBody @Valid OrderVo orderVo) { return new ArrayList<OrderVo>(){}; @RequestMapping("test") public OrderVo getTest() { return new OrderVo(); @RequestMapping("str") public String getTest2() { return "123"; @GetMapping("name") public String getDetail(@RequestParam String name) { if (name == null) { throw new BusinessException(ErrorCode.SYSTEM_ERROR); return "hello";

    需要自定义返回码时,可以实现ResultCode接口:

    public enum ErrorCode implements ResultCode {
        SYSTEM_ERROR(9999, "系统错误")
        private Integer code;
        private String message;
        ErrorCode(Integer code, String message) {
            this.code = code;
            this.message = message;
        @Override
        public Integer getCode() {
            return this.code;
        @Override
        public String getMessage() {
            return this.message;
        @Override
        public ResultCode setMessage(String s) {
            this.message = s;
            return this;
    

    使用swagger时,访问swagger-ui.html返回404,提供的解决方案是:

    @Configuration
    public class SwaggerMvnConfiguration extends WebMvcConfigurationSupport {
        @Override
        protected void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
    

    这样能解决问题,但是会覆盖引入依赖的配置,使得拦截器失效。网上说用WebMvcConfigurer,当时没有作用,试了很久发现需要加上@EnableWebMvc注解才有作用。

    @Configuration
    @EnableWebMvc
    public class SwaggerConfiguration implements WebMvcConfigurer {
         * 通用拦截器排除swagger设置,所有拦截器都会自动加swagger相关的资源排除信息
        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
            registry.addResourceHandler("/**").addResourceLocations("classpath:/META-INF/resources/").setCachePeriod(0);
    

    当同样会覆盖掉引入依赖的配置,使得拦截器失效。

    一个项目中只能有一个继承WebMvcConfigurationSupport的@Configuration类(使用@EnableMvc效果相同),如果存在多个这样的类,只有一个配置可以生效。推荐使用 implements WebMvcConfigurer 的方法自定义mvc配置。

    @Configuration
    @EnableWebMvc
    public class MyConfigurerAdapter implements WebMvcConfigurer {
        @Bean
        public ResponseResultInterceptor responseResultInterceptor() {
            return new ResponseResultInterceptor();
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(responseResultInterceptor());
        @Override
        public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
            converters.add(0, new MappingJackson2HttpMessageConverter());
    

    才有这种方式启动类可以不需要使用@Import({MyConfigurerAdapter.class})

  • 看看人家那后端API接口写得,那叫一个优雅!
  •