很多时候,后台通过ORM框架从数据库拿到表数据,需要对数据进行一些处理才抛给前端,常见的如格式化时间,在业务代码new一个SimpleDateFormat进行时间格式化处理。下面介绍以一种优雅的方式格式化出参,做一个贴心的后台程序员
出参改造前
数据库映射实体(省略get,set)
public class User {
//id主键
private Integer id;
//名称
private String name;
//生日
private Date birthday;
}
Controller
@ResponseBody
@RequestMapping("/get_users")
public List<User> getUsers() {
//模拟读取数据库数据 begin
List<User> result = new ArrayList<User>(2);
User xiaoHua = new User();
xiaoHua.setName("小花");
xiaoHua.setBirthday(new Date());
xiaoHua.setId(1);
result.add(xiaoHua);
User xiaoMing = new User();
xiaoMing.setName("小明");
xiaoMing.setBirthday(new Date());
xiaoMing.setId(2);
result.add(xiaoMing);
//模拟读取数据库数据 end
return result;
}
通过ORM框架获取两个用户对象,没有做数据格式处理,直接抛给前端
Json结果:
[
{
"birthday": 1505181715009,
"id": 1,
"name": "小花"
},
{
"birthday": 1505181715009,
"id": 2,
"name": "小明"
}
]
前端看见这个返回结果,我猜10个前端8个都黑脸,怎么说?生日(birthday)字段抛出去的是一个时间戳long类型,如果前端要显示友好标准时间格式(yyyy-MM-dd HH:mm:ss),需要做两步,1:通过时间戳new一个时间对象 2:格式化时间
格式化出参(一)
对User实体字段追加格式化注解
public class User {
// id主键
@JsonProperty(value = "objId")
private Integer id;
// 名称
private String name;
// 生日
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date birthday;
}
一些前端框架对id这一字段名比较敏感,所以这追加JsonProperty(com.fasterxml.jackson.annotation包)注解,进行别名处理;对birthday字段追加JsonFormat(com.fasterxml.jackson.annotation包)注解,对时间戳进行格式化
注:格式化注解针对出参类型为json适用,如果通过HttpServletRequest设值,需要手动转换
Json结果:
[
{
"birthday": "2017-09-12 21:55:17",
"name": "小花",
"objId": 1
},
{
"birthday": "2017-09-12 21:55:17",
"name": "小明",
"objId": 2
}
]
前端看到后台开发帮他们格式化好时间,想必忍不住要点赞
格式化出参(二)
刚才通过对映射实体追加格式化注解,达到出参格式化的目的,但此处还有需要完善的地方。假如获取用户详情API,生日birthday字段要的是yyyy-MM-dd格式的时间呢?此时@JsonFormat(pattern = “yyyy-MM-dd HH:mm:ss”, timezone = “GMT+8”)则不满足具体的业务场景了。此时我一般的做法是新建一个BO(Browse Object),不去“污染”映射实体
映射实体
public class User {
private Integer id;
// 名称
private String name;
// 生日
private Date birthday;
}
BO
public class UserBO {
// 名称
private String name;
// 生日
@JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
private Date birthday;
}
Controller
@ResponseBody
@RequestMapping("/get_users")
public List<UserBO> getUsers() {
// 模拟读取数据库数据 begin
List<User> ormResult = new ArrayList<User>(2);
User xiaoHua = new User();
xiaoHua.setName("小花");
xiaoHua.setBirthday(new Date());
xiaoHua.setId(1);
ormResult.add(xiaoHua);
User xiaoMing = new User();
xiaoMing.setName("小明");
xiaoMing.setBirthday(new Date());
xiaoMing.setId(2);
ormResult.add(xiaoMing);
// 模拟读取数据库数据 end
// 返回结果
List<UserBO> result = new ArrayList<UserBO>(ormResult.size());
for (User item : ormResult) {
UserBO bo = new UserBO();
// org.springframework.beans.BeanUtils 复制对象属性值(名称,类型一致)
BeanUtils.copyProperties(item, bo);
result.add(bo);
}
return result;
}
json结果
[
{
"birthday": "2017-09-12",
"name": "小花"
},
{
"birthday": "2017-09-12",
"name": "小明"
}
]
增加一个BO层,出参跟映射实体就解耦了,BO对象可以根据具体的业务情景选择性的返回前端所需字段(以此UserBO为例,前端可能只需显示名称,跟生日;此时id值抛出去则多余了,所以BO剔除id字段)
聊一聊
后台跟前端一直都在争议这种数据处理到底归谁来做,毕竟大家都能处理是吧。刚一开始工作,我也很“懒”不作数据的处理直接抛出前端;但逐渐地我“控制欲”越来越强,无论是数据处理还是业务的处理,能够后台做的一般都不让前端来做,怎么说?
比如安卓,IOS各一开发调用一个后台API,如果让前端自行处理,安卓跟IOS都要处理一遍,而且处理的结果可能不一致,如果让后台来做,仅仅是处理一遍,且保证处理结果一致。所以,我主张数据能够后台处理的,尽量后台处理