各位小伙伴,晚上好!这里分享下后端定义前端需传入的参数:*Command的技巧之1:使用Map<>传参和返回给前端的DTO技巧之1:自定义反序列化。
目录
一、Command定义技巧(前端需传入的参数封装)
二、自定义反序列化
三、总结
一、Command定义技巧(前端需传入的参数封装)
在大专家,后端把前端需要传入的参数多封装成Command,返回给前端的封装成DTO。
Command多用动词表示。
比如:用户更新个人信息的时候,方法如下:
public void updateUserInfo(UpdateUserInfoCommand cmd){
cmd.setUserId(getCurrentUserId());
cmd.setOperatorId(getCurrentUserId);
userInfoService.update(cmd)
}
Command命名要规范:明确是什么Command,这里是:UpdateUserInfoCommand,可能还有其他Command,不要笼统的命名为:
UserCommand.
和前端交互有传参有2种方式:
方式1:
一个Command,利用Map<String,Object>
场景:用户修改个人信息,包含各种各样的信息,各种数据格式:
"gender": "MALE",——枚举
"religiousFaith": "NOTHING", "BUDDHISM" ——字符串
"knownAllergyResource": ["鸡蛋"]——数组或容器
这么多的属性,一一封装嘛?那属性指定下来估计上百个。
用Map<>
来看一下UpdateUserInfoCommand
public class UpdateUserInfoCommand extends HashMap<String, Object> implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private String operatorId;
private UpdateOptions options;
public String getUserId() {
return StringUtils.isNotEmpty(this.userId)?this.userId:(String) get("userId");
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getOperatorId() {
return operatorId;
}
public void setOperatorId(String operatorId) {
this.operatorId = operatorId;
}
public UpdateOptions getOptions() {
return options;
}
public void setOptions(UpdateOptions options) {
this.options = options;
}
}
用Map<String,Object>传参:
优点:灵活,适合各种参数;一个接口可以搞定
缺点:前端不知道怎么传,传什么不明确,需要口头约定
方式2:
指定具体的参数,就是一个类,各种数据类型的指定即可。
方式1还存在一个问题,那就是查询返回给前端,数据怎么交互?
先来看一下返回给前端的DTO:
@JsonSerialize(using = UserDataJsonSerializer.class)
public class UserDataDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String userId;
private Map<String, Object> fields;
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public Map<String, Object> getFields() {
return fields;
}
public void setFields(Map<String, Object> fields) {
this.fields = fields;
}
public String getFieldAsString(String fieldName) {
if (fields != null) {
Object field = fields.get(fieldName);
if (field != null) {
return String.valueOf(field);
}
}
return null;
}
}
会有什么问题?这个fields会明晃晃的作为一个key传给前端,语义一点也不明确。如下:
{
"code": null,
"message": null,
"data": {
"userId": "5c333cd684ae2c88b09b69a2",
"fields": {
"liveAddress": {
"liveProvinceCode": 220000,
"liveCityCode": 220100,
"liveDistrictCode": 0,
"liveStreetCode": null,
"liveCommitteeCode": null
}
},
"headImg": null
}
}
我们要达到的效果如下:
{
"code": null,
"message": null,
"data": {
"userId": "5c333cd684ae2c88b09b69a2",
"liveAddress": {
"id": 897,
"userId": "5bde896be4b0213186390386",
"detailAddress": "上海****222",
"provinceCode": 310000,
"cityCode": 310100,
"districtCode": 310117,
"streetCode": 310117105,
"streetName": "新桥镇",
"committeeCode": 310117105002,
"committeeName": "新育居委会",
},
"headImg": null
}
}
看看这个fields,使我们想要传给前端的嘛?当然不是。我们只想给前端一个key为liveAddress,value为一个对象,该对象包含了:
liveProvinceCode等信息。怎么办?
反序列化的时候处理,不用默认的反序列化,用工具jackon。
二、自定义反序列化
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
@JsonSerialize(using = UserDataJsonSerializer.class)
public class UserDataDTO implements Serializable {
}
该注解就重写了反序列化的过程,会把fields重新处理。
public class UserDataJsonSerializer extends JsonSerializer<UserDataDTO> {
@Override
public void serialize(UserDataDTO value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeStartObject();
gen.writeStringField("userId", value.getUserId());
Map<String, Object> fields = value.getFields();
if (fields != null) {
for (Entry<String, Object> entry : fields.entrySet()) {
gen.writeFieldName(entry.getKey());
gen.writeObject(entry.getValue());
}
}
gen.writeEndObject();
}
}
使用jackson步骤总结如下:
1、定义一个类,继承JsonSerializer
2、重写serialize()方法
3、在返回给前端的DTO上增加注解:@JsonSerialize(using = UserDataJsonSerializer.class)
为什么就避免了fields传给前端呢?因为用默认的Serializable,所有属性都会直接返回,自己重写serialize方法,就可以按照想要的来反序列化拉。
总结:
1、Command命名语义明确:eg:UpdateUserInfoCommand
2、Command里参数尽量少用Map<String,Object>.优点:简化传参、简化接口数量;缺点:传递给前端的参数语义非常模糊(需约定好)、反序列化需要干预,否则出现冗余字段类似fields
3、jackson的运用