利用接口+枚举替换final int

  • 定义接口,要求必须有code和message方法
    1
    2
    3
    4
    5
    6
    7
    public interface IResultCode {

    Integer code();

    String message();

    }
  • 定义一个枚举,实现该接口
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    public enum ResultCode implements IResultCode {

    SUCCESS(1, "成功"),
    UNKNOWN_ERROR(-1, "未知异常"),

    // 参数错误
    PARAMS_IS_NULL(10001, "参数为空"),
    PARAMS_NOT_COMPLETE(10002, "参数不全"),
    PARAMS_TYPE_ERROR(1003, "参数类型错误"),
    PARAMS_IS_INVALID(10004, "参数无效"),

    // 用户错误
    USER_NOT_EXIST(20001, "用户不存在"),
    USER_NOT_LOGGED_IN(20002, "用户未登陆"),
    USER_ACCOUNT_ERROR(20003, "用户名或密码错误"),
    USER_ACCOUNT_FORBIDDEN(20004, "用户账户已被禁用"),
    USER_HAS_EXIST(20005, "用户已存在"),

    // 业务错误
    BUSINESS_ERROR(30001, "系统业务出现问题"),

    // 系统错误
    SYSTEM_INNER_ERROR(40001, "系统内部错误"),

    // 数据错误
    DATA_NOT_FOUND(50001, "数据未找到"),
    DATA_IS_WRONG(50002, "数据有误"),
    DATA_ALREADY_EXISTED(50003, "数据已存在"),

    // 接口错误
    INTERFACE_INNER_INVOKE_ERROR(60001, "系统内部接口调用异常"),
    INTERFACE_OUTER_INVOKE_ERROR(60002, "系统外部接口调用异常"),
    INTERFACE_FORBIDDEN(60003, "接口禁止访问"),
    INTERFACE_ADDRESS_INVALID(60004, "接口地址无效"),
    INTERFACE_REQUEST_TIMEOUT(60005, "接口请求超时"),
    INTERFACE_EXCEED_LOAD(60006, "接口负载过高"),

    // 权限错误
    PERMISSION_NO_ACCESS(70001, "没有访问权限"),

    ;
    final Integer code;
    final String message;

    ResultCode(Integer code, String message) {
    this.code = code;
    this.message = message;
    }

    public Integer code() {
    return code;
    }

    public String message() {
    return message;
    }

    }
  • 定义返回结果实体类
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    import com.fasterxml.jackson.annotation.JsonInclude;
    import lombok.Data;

    import java.io.Serializable;

    @Data
    public class Result implements Serializable {

    private int code;
    private String message;

    @JsonInclude(JsonInclude.Include.NON_NULL)
    private Object data;

    public Result(IResultCode resultCode, Object data) {
    this.code = resultCode.code();
    this.message = resultCode.message();
    this.data = data;
    }

    public Result(IResultCode resultCode) {
    this(resultCode, null);
    }

    }
  • OK,接下来只要按照下面的方式使用即可了
    1
    2
    3
    4
    @GetMapping
    Result say(){
    return new Result(ResultCodeBasic.SUCCESS,"Hello,world!");
    }

利用自定义注解返回通用结果

  • 方法直接返回Result类的话可读性很差
    所以现在迫切需要一个类似于@ResponseBody的注解,标注 某个类所有方法或某个方法 应该返回Result
    emm…就叫他 @ResponseResult
    1
    2
    3
    4
    5
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface ResponseResult {
    }
  • 然后实现ResponseBodyAdvice对其进行增强
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    import org.springframework.core.MethodParameter;
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.server.ServerHttpRequest;
    import org.springframework.http.server.ServerHttpResponse;
    import org.springframework.web.bind.annotation.ControllerAdvice;
    import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

    @ControllerAdvice
    public class ResponseResultAdvice implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
    return methodParameter.hasMethodAnnotation(ResponseResult.class)
    || methodParameter.getContainingClass().isAnnotationPresent(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    return new Result(ResultCode.SUCCESS, body);
    }

    }
  • 现在,如果方法的返回值不是 String 类型已经能正常使用了
    但是如果方法返回了一个 String ,比如下面这个,就会抛出 Result cannot be cast to java.lang.String 异常
    1
    2
    3
    4
    @GetMapping
    String say() {
    return "Hello,world!";
    }
    原因是:StringHttpMessageConverter 要比其它的 HttpMessageConverter 排得靠前一些 (参考
    有三种解决方式

    直接删除 StringHttpMessageConverter

    1
    2
    3
    4
    5
    6
    7
    8
    //作为ResponseResultAdvice的内部类
    @Configuration
    static class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.removeIf(c -> c instanceof StringHttpMessageConverter);
    }
    }

    MappingJackson2HttpMessageConverter提到StringHttpMessageConverter之前

    1
    2
    3
    4
    5
    6
    7
    8
    //作为ResponseResultAdvice的内部类
    @Configuration
    static class WebConfiguration implements WebMvcConfigurer {
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
    converters.add(0, new MappingJackson2HttpMessageConverter());
    }
    }

    在beforeBodyWrite判断一下是否为string,进行单独的json序列化

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    @Autowired
    ObjectMapper objectMapper;

    @SneakyThrows
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
    if (body instanceof String)
    return objectMapper.writeValueAsString(new Result(ResultCode.SUCCESS, body));
    return new Result(ResultCode.SUCCESS, body);
    }

绑定异常和通用结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import java.util.Map;

@ResponseBody
@ControllerAdvice
public class ExceptionCatcher {

//日志记录
private static final Logger logger = LoggerFactory.getLogger(ExceptionCatcher.class);
//异常类型和结果的映射
private static final Map<Class<? extends Throwable>, Result> exceptions = ImmutableMap.<Class<? extends Throwable>, Result>builder()
.put(ArithmeticException.class, new Result(ResultCode.BUSINESS_ERROR))
.build();
private static final Result unknown = new Result(ResultCode.UNKNOWN_ERROR);

//全局异常处理
@ExceptionHandler(Exception.class)
private Result exception(Exception e) {
logger.error(e.getMessage());
Result result = exceptions.get(e.getClass());
return result != null ? result : unknown;
}

}