zoukankan      html  css  js  c++  java
  • 一种统一的Controller响应模式设计

    1,概述

    Controller响应主要涉及两个方面:a)业务码和异常信息处理;b)正确的响应数据返回。

    2,定义统一的业务码编码规范

    定义全平台所有系统遵循的统一的业务码(长度9位)规范(表1):

    平台编号(1位)

    模块编码(2位)

    错误级别(1位)

    错误类型(2位)

    错误内容(3位)

     

     

    以下已IoT平台为例说明

    表2

    IoT

    模块

    平台编号

    模块编码

    Device

    2

    10

    Group

    20

    Share

    30

    Control

    40

    OTA

    50

    Resource

    60

    表3

    错误级别

    编码

    错误类型

    编码

    不重要

    0

    业务错误

    10

    一般

    1

    中间件错误

    20

    重要

    2

    数据库错误

    30

    缓存错误

    40

    第三方依赖错误

    50

    外部系统错误

    60

    网络错误

    70

    安全规范错误

    80

    未知类型错误

    90

    注:0代表success,错误码使用Integer类型

    3,领域模型设计

    该设计方案主要是定义了1个接口,5枚举类,1个异常类,1个可序列化类:

    • Platform、Module、Level和Category枚举类是依据表1的规范,定义的业务码构成项;
    • ResponseCode接口定义了统一的业务码;
    • ErrorCode枚举类实现了ResponseCode接口,这意味着每一个ErrorCode对象都是单例的且符合平台统一的业务码规范;
    • CodeException异常类是一个运行时异常类,并且只允许使用ResponseCode来创建实例;
    • WebResult<T>可序列化的模板类是Controller统一的响应(包含异常的)结果,并且WebResult只允许通过公开的静态方法来创建。

    以下以基本的IoT平台来看源码设计:

    4,ResponseCode interface

    /**
    * api统一返回的响应码
    */
    public interface ResponseCode {

    /**
    * 最终返回的统一错误应当是{@link Integer#parseInt(String value)},
    * 在这里value应该为{@link ResponseCode#prefix() + code}
    * @return
    */
    int code();
    String msg();

    default Platform platform() {return null;};
    default Module module(){return null;};
    default Level level(){return null;};
    default Category category(){return null;};

    default String prefix() {
    return platform().code + module().code + level().code + category().code;
    }
    //全平台唯一的成功响应码
    ResponseCode SUCCESS = new ResponseCode() {
    @Override
    public int code() {
    return 0;
    }
    @Override
    public String msg() {
    return null;
    }
    };

    enum Platform {
    IoT("2");
    public String code;
    Platform(String code) {
    this.code = code;
    }
    }

    enum Module {
    Device("10"), Group("20"),
    Share("30"), Control("40"),
    Resource("50"), OTA("60");
    public String code;
    Module(String code) {
    this.code = code;
    }
    }
    enum Level {
    Ignored("0"), Normal("1"), Important("2");
    public String code;
    Level(String code) {
    this.code = code;
    }
    }
    enum Category {
    Biz("10", "业务错误"), Middleware("20", "中间件错误"), DB("30", "数据库错误"),
    Cache("40", "缓存错误"), Dependency("50", "外部依赖错误"), Connection("60", "网络错误"),
    Security("70", "安全规范错误"), Unknown("80", "未知类型错误");
    public String code;
    public String text;
    Category(String code, String text) {
    this.code = code;
    this.text = text;
    }
    }
    }

    该接口的代码没有太多争议,可能会有疑惑的是,为什么platform(), module(),level()和category()给了默认返回空的实现。这样做的目的,完全是为了使ResponseCode.SUCCESS的代码简洁一些。

    5,ErrorCode枚举类(Resource模块)

    public enum ErrorCode implements ResponseCode {

    UNKNOWN_ERROR(Level.Important, Category.Unknown,"000","内部系统未知错误"),

    UNKNOWN_RESOURCE(Level.Normal, Category.Biz, "001", "无效资源");

    String code;
    String msg;
    Level level;
    Category category;

    ErrorCode(Level level, Category category, String code, String msg) {
    this.code = code;
    this.msg = msg;
    this.level = level;
    this.category = category;
    }

    @Override
    public int code() {
    return Integer.parseInt(this.prefix() + this.code);
    }

    @Override
    public String msg() {
    return this.msg;
    }

    @Override
    public Platform platform() {
    return Platform.IoT;
    }

    @Override
    public Module module() {
    return Module.Resource;
    }

    @Override
    public Level level() {
    return this.level;
    }

    @Override
    public Category category() {
    return this.category;
    }
    }

    上述使用枚举类实现ResponeCode接口来定义的业务码,比较清晰明了的遵照表1的规范定义业务码。

    6,CodeException异常类

    该异常类也比较简单,如上所述,它是一个运行时的异常类,并且只允许使用ResponseCode来创建实例,一切的控制都为了让代码按照业务规范来编写。如此,无论代码编写在任何一层,都可以抛出统一规范的业务码,来终止程序的继续进行;同时,在业务代码的最顶层(即Controller层),(下文将给出的WebResult<T>)就可以实现统一业务码和异常信息处理了。

    7,WebResult<T>类

    public class WebResult<T> implements Serializable {

    Integer errorCode;

    String errorMessage;

    Long timestamp;

    T data;

    private WebResult(ResponseCode responseCode, T data){
    this.errorCode = responseCode.code();
    this.errorMessage = responseCode.msg();
    this.timestamp = new Date().getTime();
    this.data = data;
    }

    public static WebResult success() {
    WebResult result = new WebResult(ResponseCode.SUCCESS, null);
    return result;
    }

    public static <T> WebResult<T> execute(Function function, String exceptionLog, Logger logger) {
    WebResult<T> result = WebResult.success();
    try {
    function.function(result);
    } catch (CodeException ex) {
    logger.warn(exceptionLog+": {}",ex);
    result.setErrorCode(ex.getCode().code());
    result.setErrorMessage(ex.getCode().msg());
    }
    return result;
    }

    public static WebResult execute(Function function, String exceptionLog, Logger logger,
    ResponseCode unhandledCode) {
    WebResult result = WebResult.success();
    result.setData(null);
    execute(function, exceptionLog, logger, unhandledCode, result);
    return result;
    }

    public static WebResult exec4ArrayData(Function function, String exceptionLog, Logger logger,
    ResponseCode unhandledCode) {
    WebResult<List> result = WebResult.success();
    result.setData(Collections.emptyList());
    execute(function, exceptionLog, logger, unhandledCode, result);
    return result;
    }

    private static void execute(Function function, String exceptionLog, Logger logger,
    ResponseCode unhandledCode, WebResult result) {
    try {
    function.function(result);
    } catch (CodeException ex) {
    logger.warn(exceptionLog+": {}",ex);
    result.setErrorCode(ex.getCode().code());
    result.setErrorMessage(ex.getCode().msg());
    } catch (Exception e) {
    logger.warn(exceptionLog+": {}",e);
    result.setErrorCode(unhandledCode.code());
    result.setErrorMessage(unhandledCode.msg());
    }
    }

    public interface Function {
    void function(WebResult result);
    }
    // for json mapper
    public int getErrorCode() {
    return errorCode;
    }

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

    public String getErrorMessage() {
    return errorMessage;
    }

    public void setErrorMessage(String errorMessage) {
    this.errorMessage = errorMessage;
    }

    public T getData() {
    return data;
    }

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

    public Long getTimestamp() {
    return timestamp;
    }

    public void setTimestamp(Long timestamp) {
    this.timestamp = timestamp;
    }
    }

    从上述WebResult的代码定义可知,它使用了Template模式来实现对Controller响应结果的统一处理。具体下面可以看看Controller层是如何使用WebResult来统一处理响应的:

  • 相关阅读:
    Python微信机器人
    Jumpserver开源跳板机系统介绍
    Django---django-rest-framework(drf)-luffycity projects
    Linux-Mysql 遗忘密码如何解决?
    up line
    linux
    vue中computed(计算属性)
    input框在浏览器上显示一个叉,去掉方法
    如何通过命令行来克隆git
    手机抓包fiddler配置及使用教程
  • 原文地址:https://www.cnblogs.com/shenjixiaodao/p/12667871.html
Copyright © 2011-2022 走看看