一种统一的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来统一处理响应的:

原文地址:https://www.cnblogs.com/shenjixiaodao/p/12667871.html