
//各种响应方式如下
@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; } }
|
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南