赵走x博客
网站访问量:151826
首页
书籍
软件
工具
古诗词
搜索
登录
32、使用MyBatis注解实现数据库操作
31、MyBaties使用XML配置文件实现数据库操作
30、Spring Boot构建MyBatis应用程序
29、MyBatis简介
28、实战:实现JdbcTemplate多数据源
27、使用JdbcTemplate操作数据库
26、JdbcTemplate入门
25、实战:实现Web API版本控制
24、使用Swagger生成Web API文档
23、Thymeleaf页面布局
22、Thymeleaf内置对象、内嵌变量
21、Thymeleaf内联
20、Thymeleaf语法
19、Thymeleaf表达式
18、Thymeleaf入门
17、全局异常处理
16、实战:实现优雅的数据返回
15、跳转指定页面
14、Spring Boot静态资源
13、Spring Boot数据转换配置
12、跨域访问
11、Web配置
10、过滤器
9、拦截器
8、数据验证
7、参数传递
6、URL映射
5、@ResponseBody
4、@RequestMapping
3、@Controller和@RestController
2、Web项目结构
1、spring-boot-starter-web介绍
16、实战:实现优雅的数据返回
资源编号:551785
热度:112
16、实战:实现优雅的数据返回
本节介绍如何让前后台优雅地进行数据交互,正常的数据如何统一数据格式,以及异常情况如何统一处理并返回统一格式的数据。 # 一、为什么要统一返回值 在项目开发过程中经常会涉及服务端、客户端接口数据传输或前后台分离的系统架构下的数据交互问题。 如何确保数据完整、清晰易懂是考验开发者的大难题。定义统一的数据返回格式有利于提高开发效率、降低沟通成本,降低调用方的开发成本。 目前比较流行的是基于JSON格式的数据交互。但是JSON只是消息的格式,其中的数据内容还需要重新设计和定义。无论是HTTP接口还是RPC接口,保持返回值格式统一很重要。 在项目中,我们会将响应封装成JSON返回,一般会统一所有接口的数据格式,使前端(iOS、Android、Web)对数据的操作一致、轻松 。一般情况下,统一返回数据格式没有固定的规范,只要能描述清楚返回的数据状态以及要返回的具体数据即可,但是一般会包含状态码、消息提示语、具体数据这3部分内容。 例如,一般的系统要求返回的基本数据格式如下: ``` { "code": 20000, "message": "成功", "data": { "items": [ { "id": "1", "name": "weiz", "intro": "备注" } ] } } ``` 通过上面的示例我们知道,定义的返回值包含4要素:响应结果、响应码、消息、返回数据。 # 二、统一数据返回 前面介绍了为什么要统一返回值以及如何实现统一JSON数据返回。 接下来通过示例演示如何实现统一JSON数据返回。 ### 1. 定义数据格式 定义返回值的基本要素,确保后台执行无论成功还是失败都是返回这些字段,而不会出现其他的字段。 定义的返回值包含如下内容: - Integer code:成功时返回0,失败时返回具体错误码。 - String message:成功时返回null,失败时返回具体错误消息。 - T data:成功时返回具体值,失败时为null。 根据上面的返回数据格式的定义,实际返回的数据模板如下: ``` { "code": 20000, "message": "成功", "data": { "items": [ { "id": "1", "name": "weiz", "intro": "备注" } ] } } ``` 其中,data字段为泛型字段,根据实际的业务返回前端需要的数据类型。 ### 2. 定义状态码 返回的数据中有一个非常重要的字段:状态码。 状态码字段能够让服务端、客户端清楚知道操作的结果、业务是否处理成功,如果失败,失败的原因等信息。 所以,定义清晰易懂的状态码非常重要。状态码定义如表4-3所示。  以上定义的是通用状态码,其他的业务相关状态码需要根据实际业务定义。 ### 3. 定义数据处理类 前面定义了返回数据的格式和处理结果的状态码,接下来定义通用的结果处理类。在实际使用时可以根据情况处理。本示例中简单定义如下: ``` /** * * @Title: JSONResult.java * @Package com.weiz.utils * @Description: 自定义响应数据结构 * 200:表示成功 * 500:表示错误,错误信息在msg字段中 * 501:bean验证错误,无论多少个错误都以map形式返回 * 502:拦截器拦截到用户token出错 * 555:异常抛出信息 * Copyright: Copyright (c) 2016 * * @author weiz * @date 2016年4月22日 下午8:33:36 * @version V1.0 */ public class JSONResult { // 定义jackson对象 private static final ObjectMapper MAPPER = new ObjectMapper(); // 响应业务状态 private Integer code; // 响应消息 private String msg; // 响应中的数据 private Object data; public static JSONResult build(Integer status, String msg, Object data) { return new JSONResult(status, msg, data); } public static JSONResult ok(Object data) { return new JSONResult(data); } public static JSONResult ok() { return new JSONResult(null); } public static JSONResult errorMsg(String msg) { return new JSONResult(500, msg, null); } public static JSONResult errorMap(Object data) { return new JSONResult(501, "error", data); } public static JSONResult errorTokenMsg(String msg) { return new JSONResult(502, msg, null); } public static JSONResult errorException(String msg) { return new JSONResult(555, msg, null); } public JSONResult() { } public JSONResult(Integer status, String msg, Object data) { this.status = status; this.msg = msg; this.data = data; } public JSONResult(Object data) { this.status = 200; this.msg = "OK"; this.data = data; } public Boolean isOK() { return this.status == 200; } /** * * @Description: 将json结果集转化为JSONResult对象 * 需要转换的对象是一个类 * @param jsonData * @param clazz * @return * * @author weiz * @date 2016年4月22日 下午8:34:58 */ public static JSONResult formatToPojo(String jsonData, Class> clazz) { try { if (clazz == null) { return MAPPER.readValue(jsonData, JSONResult.class); } JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = jsonNode.get("data"); Object obj = null; if (clazz != null) { if (data.isObject()) { obj = MAPPER.readValue(data.traverse(), clazz); } else if (data.isTextual()) { obj = MAPPER.readValue(data.asText(), clazz); } } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } } /** * * @Description: 没有object对象的转化 * @param json * @return * * @author weiz * @date 2016年4月22日 下午8:35:21 */ public static JSONResult format(String json) { try { return MAPPER.readValue(json, JSONResult.class); } catch (Exception e) { e.printStackTrace(); } return null; } /** * * @Description: Object是集合转化 * 需要转换的对象是一个list * @param jsonData * @param clazz * @return * * @author weiz * @date 2016年4月22日 下午8:35:31 */ public static JSONResult formatToList(String jsonData, Class> clazz) { try { JsonNode jsonNode = MAPPER.readTree(jsonData); JsonNode data = jsonNode.get("data"); Object obj = null; if (data.isArray() && data.size() > 0) { obj = MAPPER.readValue(data.traverse(), MAPPER.getTypeFactory().constructCollectionType (List.class, clazz)); } return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj); } catch (Exception e) { return null; } } public String getOk() { return ok; } public void setOk(String ok) { this.ok = ok; } } ``` 上面定义了数据返回处理类,定义了响应数据结构,所有接口的数据返回统一通过此类处理,接收此类数据后,需要使用本类的方法转换成对应的数据类型格式(类或者list)。 ### 4. 处理数据返回 定义数据处理类后,在控制器中将返回的数据统一加上数据处理。调用如下: ``` @RequestMapping("/getUser") public JSONResult getUserJson(){ User u = new User(); u.setName("weiz222"); u.setAge(20); u.setBirthday(new Date()); u.setPassword("weiz222"); return JSONResult.ok(u); } ``` ### 5. 测试 启动helloworld项目,浏览器中访问http://localhost:8080/user/getUser ,页面数据返回如下: ``` { "code": 200, "msg": "OK", "data": { "name": "weiz222", "age": 20, "birthday": "2020-12-21 06:57:13" } } ``` 返回的结果数据在正常的时候能够按照我们的预期结果格式返回。