设计 构建 java 统一异常 体系 服务器统一返回格式

1、服务器返回格式

系统统一返回JSON数据格式:

success: {"status":"success","data":{"name":"test"}}

fail:{"status":"fail","errorCode":900001,"errorMsg":"参数错误"}

备注:请求成功,服务器返回 status状态位以及主要报文体data;请求失败,则返回status状态位、错误code码以及错误含义,不返回主要报文data(可选、可控),客户端通过解析status判断请求是否成功,然后做出相应的动作。

2、服务器返回数据工具实体辅助类

response 顶层类:

public class Response implements Serializable {
    private static final long serialVersionUID = 1L;
    protected transient String SUCCESS_STATUS = "success";
    protected transient String FAIL_STATUS = "fail";
    private String status;

    public Response() {
        this.status = SUCCESS_STATUS;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

}

成功:

public class SuccessResponse extends Response {
    private static final long serialVersionUID = 1L;
    private Object data;

    public SuccessResponse(Object data) {
        this.data = data;
        this.setStatus(SUCCESS_STATUS);
    }

    public Object getData() {
        return data;
    }

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

失败:

public class FailResponse extends Response {
    private static final long serialVersionUID = 1L;
    private Integer errorCode;
    private String errorMsg;

    public FailResponse() {

    }

    public FailResponse(Integer errorCode, String errorMsg) {
        this.errorCode = errorCode;
        this.errorMsg = errorMsg;
        this.setStatus(FAIL_STATUS);
    }

    public Integer getErrorCode() {
        return errorCode;
    }

    public void setErrorCode(Integer errorCode) {
        this.errorCode = errorCode;
    }

    public String getErrorMsg() {
        return errorMsg;
    }

    public void setErrorMsg(String errorMsg) {
        this.errorMsg = errorMsg;
    }
}

response响应工具辅助类:

public class ResponseUtil {


    public static Response success() {
        return new Response();
    }

  
    public static Response success(Object data) {
        return new SuccessResponse(data);
    }

  
    public static Response fail(Integer errorCode, String errorMsg) {
        return new FailResponse(errorCode, errorMsg);
    }

}

3、系统异常定义,使用enum存储

public enum ErrorType {
    SYSTEM_ERROR(800,"系统错误","system error"),
    NO_USER(90003,"该用户不存在","user does not exist"),
    REGISTERED_PHONE(90004,"手机号已被注册","phone number has been registered"),
    ;
    
    private Integer code;
    private String zhCnMsg;
    private String enMsg;

    ErrorType(Integer code, String zhCnMsg, String enMsg) {
        this.code = code;
        this.zhCnMsg = zhCnMsg;
        this.enMsg = enMsg;
    }
    
    public static ErrorType get(Integer code) {
        for (ErrorType errorType : ErrorType.values()) {
            if (errorType.getCode().equals(code)) {
                return errorType;
            }
        }
        return null;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getZhCnMsg() {
        return zhCnMsg;
    }

    public void setZhCnMsg(String zhCnMsg) {
        this.zhCnMsg = zhCnMsg;
    }

    public String getEnMsg() {
        return enMsg;
    }

    public void setEnMsg(String enMsg) {
        this.enMsg = enMsg;
    }
}

全局异常-->BSExceptions:

public class BSException extends RuntimeException {
    private static final long serialVersionUID = 1L;
    private ErrorType errorType;

    public BSException () {

    }

    public BSException (String message) {
        super(message);
    }

    public BSException (Throwable cause) {
        super(cause);
    }

    public BSException (ErrorType errorType) {
        this.errorType = errorType;
    }

    public BSException (ErrorType errorType, String message) {
        super(message);
        this.errorType = errorType;
    }

    public static BSException error(ErrorType error) {
        return new BSException (error);
    }

    public static BSException error(ErrorType error, String message) {
        return new BSException (error, message);
    }

    public static BSException systemError() {
        return new BSException (ErrorType.SYSTEM_ERROR);
    }

    public ErrorType getErrorType() {
        return errorType;
    }

    public void setErrorType(ErrorType errorType) {
        this.errorType = errorType;
    }
}

4、使用(不使用spring异常体系,抓取不全)

以上为系统设计数据结构相关,项目设计一个Filter主入口,定义在web.xml中,url-pattern-> /*,

doFilter()->执行处理相关业务逻辑:

public class BStFilter implements Filter {

   //slf4j--log4j2
    private Logger logger = LoggerFactory.getLogger(getClass());
    private final String ASSETS_PREFIX = "/assets/**";
    private PathMatcher pathMatcher;
    private DefaultServletHttpRequestHandler resourceHandler;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        String servletPath = request.getServletPath();
        String contextPath = request.getContextPath();
        request.setAttribute("ctx", contextPath);
        boolean process = true;
        try {
            if (assets(servletPath)) {

                //静态资源直接交给web容器处理,不用再进入spring-->mvc:resource
                resourceHandler.handleRequest(request, response);
                return;
            } else {
                /**处理一些业务逻辑,如权限等认证**/
            if (process) {
                chain.doFilter(request, response);
            }
        } catch (Exception e) {

            //处理系统内部出现的各种异常
            doException(request, response, e);
        } finally {
            reset();
        }
    }

    /**
     * 判断是否为静态资源
     * 
     * @param path
     * @return
     */
    protected boolean assets(String path) {
        return match(ASSETS_PREFIX, path);
    }

    private boolean match(String pattern, String path) {
        return pathMatcher.match(pattern, path);
    }

    /**
     * 
     * @param request
     * @param response
     * @param e
     */
    protected void doException(HttpServletRequest request, HttpServletResponse response, Exception e) {
        ErrorType errorType = null;

        //org.apache.commons.lang3.exception.ExceptionUtils;
        Throwable rootException = ExceptionUtils.getRootCause(e);
        String extMsg = null;
        if (rootException instanceof BSException) {
            BSException ex = (BSException) rootException;
            errorType = ex.getErrorType();
            extMsg = ex.getMessage();
            logException(rootException, errorType);
        } else {
            //e.printStackTrace();
            errorType = ErrorType.SYSTEM_ERROR;
        }
            Integer code = errorType.getCode();
            String msg = errorType.getZhCnMsg();
            if (StringUtils.isNotBlank(extMsg)) {
                msg = msg.concat(":").concat(extMsg);
            }
            WebUtil.renderJson(response, ResponseUtil.fail(code, msg));
            return;
    }

    /**
     * 
     * @param throwable
     * @param errorType
     */
    protected void logException(Throwable throwable, ErrorType errorType) {
        String className = throwable.getStackTrace()[1].getClassName();
        String methodName = throwable.getStackTrace()[1].getMethodName();
        int lineNumbe = throwable.getStackTrace()[1].getLineNumber();
        String nowTime = DateTimeUtil.getNowTime();
        Integer code = errorType.getCode();
        String enMsg = errorType.getEnMsg();
        String zhCnMsg = errorType.getZhCnMsg();
        String extMsg = throwable.getMessage();
        if (StringUtils.isNotBlank(extMsg)) {
            zhCnMsg = zhCnMsg.concat(":").concat(extMsg);
        }
        String classLog = "{} exception at:{}.{}:{}";
        String typeLog = "{} errorType:{code:{},enMsg:{},cnMsg:{}}";
        logger.error(classLog, nowTime, className, methodName, lineNumbe);
        logger.error(typeLog, nowTime, code, enMsg, zhCnMsg);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        ServletContext context = filterConfig.getServletContext();

       //org.springframework.util.PathMatcher;

       //org.springframework.util.AntPathMatcher;
        pathMatcher = new AntPathMatcher();//spring Ant风格匹配处理器

       //spring 内部默认servletHandler

       //参考org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler
        resourceHandler = new DefaultServletHttpRequestHandler();
        resourceHandler.setServletContext(context);
    }

    @Override
    public void destroy() {

    }

    private void reset() {
        //释放一些资源
    }

}

public abstract class WebUtil {
    private static String CHARSET = Constants.DEFAULT_CHARSET_NAME;


    public static void renderJson(HttpServletResponse response, Object obj) {
        renderJson(response, obj, CHARSET);
    }

   
    public static void renderJson(HttpServletResponse response, Object data, String charset) {
        try {
            render(response, JsonUtil.toJson(data), "Content-Type", "text/json;charset=" + CHARSET);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    
    public static void render(HttpServletResponse response, String content, String headerName, String headerValue) {
        response.addHeader(headerName, headerValue);
        response.setStatus(200);
        render(response, content);
    }

    
    private static void render(HttpServletResponse response, String content) {
        try {
            PrintWriter pw = response.getWriter();
            pw.write(content);
            pw.flush();
            pw.close();
            return;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

//方便替换其它json工具类,jackson或fastjson

//更换此类的json工具,外部不用改变代码,便于整体更换json框架

public abstract class JsonUtil {

   //com.alibaba.fastjson.TypeReference

  //com.alibaba.fastjson.parser.Feature

//com.alibaba.fastjson.serializer.SerializerFeature
    private static SerializerFeature[] serializerFeatures;
    private static Feature[] features;

    static {
        serializerFeatures = new SerializerFeature[] { SerializerFeature.WriteMapNullValue,
                SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty,
                SerializerFeature.WriteNullNumberAsZero, SerializerFeature.DisableCircularReferenceDetect };

        features = new Feature[] {

        };
    }

    /**
     * 序列化
     * 
     * @param obj
     * @return
     */
    public static String toJson(Object obj) {

        //com.alibaba.fastjson.JSON
        return JSON.toJSONString(obj, serializerFeatures);
    }

}
 

public abstract class Constants {
    public static Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
    public static final String DEFAULT_CHARSET_NAME = DEFAULT_CHARSET.name();
}

public abstract class DateTimeUtil {

    /**
     * yyyy-MM-dd HH:mm:ss
     * 
     * @return
     */
    public static String getNowTime() {

       //org.joda.time.DateTime
        return DateTime.now().toString(""yyyy-MM-dd HH:mm:ss"");
    }

}

5、内部流转调用

以上客户端请求有入口filter进入,而后进入系统内部,故内部抛出的各种异常都能捕获,再有入口filter处理,返回客户端统一格式响应报文。

filter-->doFilter()-->chain.doFilter(request, response);执行到springMVC系统内部,controller-->service-->dao

@Controller
@RequestMapping("/bs")
public class BSController{

   @Autowired
    private BSService bSService;

   @PostMapping("/test")
   public Response test() {
        bSService.test();
        return ResponseUtil.success();
    }

  @PostMapping("/test1")
   public Response test1() {
        return ResponseUtil.success(bSService.test1(););
    }

}

@Service
public class BSServiceImpl implements BSService {
    @Autowired
    private BSDAO bSDAO;

    @Override
    public void test() {
        String phone = "";
        String password ="";

        //不存在胡写的一个类,主要模拟用户查找
        Test test = bSDAO.findByPhone(phone);
        if (test != null) {

            //用户不存在抛对应存储异常,传递到入口filter,而后执行doException,返回客户端标准json
            throw BSException .error(ErrorType.REGISTERED_PHONE);
        }

    //异常抛完并处理,底下的业务逻辑不走了

     //处理业务逻辑
    }

  @Override
    public TestResponse test2() {
        TestResponse response = new TestResponse();
        Test test = bSDAO.findById(1);
        if (test != null) {

            //org.springframework.beans.BeanUtils
            BeanUtils.copyProperties(test, response);
        }else{

            //用户不存在抛对应存储异常,传递到入口filter,而后执行doException,返回客户端标准json
            throw BSException .error(ErrorType.NO_USER, "自定义其它错误说明");

        }
        return response;
    }
}

6、解释

进入系统业务处理内部,一些胡求定义的不存在的类,是为了说明一些业务逻辑。总体讲来就是,利用异常体系处理各种业务返回错误码,入口filter处理了所有(除了自身不可预测的错误)的错误返回码,正确的响应有springMVC json 处理返回客户端,其中其它含义原理由读者自行体会,希望对有需要的人有帮助。

猜你喜欢

转载自my.oschina.net/u/1434882/blog/1787098
今日推荐