240
世界上有2种人,一种懂二进制,另一种不懂二进制。

拙见--springMVC controller响应方式及统一的controller响应模式

//各种响应方式如下
@Controller
public class HelloController { //最原始的响应 @RequestMapping("/getOriginalResponse") public void getOriginalResponse(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setCharacterEncoding("utf-8");//设置获取请求参数值的编码 response.setContentType("application/json;charset=utf-8"); //不设置此行编码则浏览器出现中文乱码 response.getWriter().print("getOriginalResponse原始响应"); } //返回ModelAndView @RequestMapping("/getModelAndView") public ModelAndView getModelAndView() { ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("paramName", "张三"); modelAndView.setViewName("welcome"); return modelAndView; } //转发 @RequestMapping("/getRequestDispatcher") public void getRequestDispatcher(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { request.setAttribute("user", "李四"); request.getRequestDispatcher("/WEB-INF/views/welcome.jsp").forward(request, response); } //转发 @RequestMapping("/getUiModelAttribute") public String getUiModelAttribute() { return "welcome";//= return "forward:welcome.jsp" } //重定向 @RequestMapping("/sendRedirect") public String sendRedirect(HttpServletResponse response) throws ServletException, IOException { //重定向是俩次请求,取不到request里面的参数值 return "redirect:welcome.jsp";//=response.sendRedirect("welcome.jsp") } //下载文件 @RequestMapping("/downloadFile") public void downloadFile(OutputStream outputStream) throws IOException { Files.copy(Paths.get("D:\\软件按转包\\jr-ide-intellij-6.4.3_13-16.zip"), outputStream); } }

往往实际开发中响应到前端的数据格式要符合业务响应规范

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;
}
}
posted @ 2022-06-16 16:48  _Origin  阅读(339)  评论(0编辑  收藏  举报