我们在写API接口时,有这样的需求,就是根据客户端选择的字段进行值返回;例如:我们调用淘宝的接口,由于接口字段太多,调用方可能指定需要返回的字段,接口返回相应的字段结果,今天自己要做一个简单实现:
@Autowired
OrderServiceImpl orderService;
// 简单顶一个接口,参数中定义需要返回的字段
@PostMapping("/test")
private List<JSONObject> test(@RequestBody RequestParamVO paramVO){
return orderService.getOrderList(paramVO);
}
@Data
public class RequestParamVO {
// 需要返回的字段,我们可能通过定义在枚举中,用户选择之后传回
private List<String> fields;
}
需要做的步骤:
- 首选要校验客户端传来的字段,是不是我们定义的字段,如果不是我们定义的字段,就移除这个字段,也方式SQL注入;我们将全部字段定义在OrderPO这个实体对象中;
public class OrderPO{
private Long id;
private String orderNo;
private String name;
private String phone;
private String province;
private String city;
private String county;
private String address;
private BigDecimal orderPrice;
private Integer status;
}
// 验证参数,出入的字段是对象中存在的字段,防止SQL注入
List<String> fields = paramVO.getFields();
// 我们把所有的字段定义在orderPO或者一个枚举中
Field[] declaredFields = OrderPO.class.getDeclaredFields();
Set<String> fieldSet = Arrays.stream(declaredFields).map(info -> info.getName()).collect(Collectors.toSet());
// 字段校验,过滤不存在的字段
Set<String> existFieldSet = fields.stream().filter(info -> fieldSet.contains(info)).collect(Collectors.toSet());
,,, 等等
- 第二步:根据用户自定的字段做mysql数据库查询,其中returnField就是根据用户定义字段转换而来
// 这里通过SQL查询,需要返回的字段,转换为SQL查询字符串: order_no,name,phone,order_price
String returnField = existFieldSet.stream().map(info -> humpToLine(info)).collect(Collectors.joining(","));
<select id="getOrderList" resultType="com.xxx.OrderPO">
select
${returnField}
from order
limit 10
</select>
- 第三步:就查询接口通过反射原因获取所需要的字段,封装到JSONObject对象中进行返回;
private static Pattern humpPattern = Pattern.compile("[A-Z]");
// 将驼峰命名转为下划线名称
public static String humpToLine(String str) {
Matcher matcher = humpPattern.matcher(str);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
matcher.appendReplacement(sb, "_" + matcher.group(0).toLowerCase());
}
matcher.appendTail(sb);
return sb.toString();
}
@Autowired
TestMapper testMapper;
public List<JSONObject> getOrderList(RequestParamVO paramVO){
// 验证参数,出入的字段是对象中存在的字段,防止SQL注入
List<String> fields = paramVO.getFields();
// 我们把所有的字段定义在orderPO或者一个枚举中
Field[] declaredFields = OrderPO.class.getDeclaredFields();
Set<String> fieldSet = Arrays.stream(declaredFields).map(info -> info.getName()).collect(Collectors.toSet());
// 字段校验,过滤不存在的字段
Set<String> existFieldSet = fields.stream().filter(info -> fieldSet.contains(info)).collect(Collectors.toSet());
// 这里通过SQL查询,需要返回的字段,转换为SQL查询字符串: order_no,name,phone,order_price
String returnField = existFieldSet.stream().map(info -> humpToLine(info)).collect(Collectors.joining(","));
// 从mysql中查询数据
List<OrderPO> orderList = testMapper.getOrderList(returnField);
List<JSONObject> result = new ArrayList<>();
if(!CollectionUtils.isEmpty(orderList)){
for (OrderPO orderPO : orderList) {
Field[] orderPOField = orderPO.getClass().getDeclaredFields();
JSONObject object = new JSONObject();
Arrays.stream(orderPOField).filter(info->existFieldSet.contains(info.getName()))
.forEach(info->{
try {
info.setAccessible(true);
object.put(info.getName(),info.get(orderPO));
} catch (IllegalAccessException e) {
log.error("字段转换异常");
}
});
result.add(object);
}
}
return result;
}