飞道的博客

前后端分离之JSON数据交互格式抉择

424人阅读  评论(0)

场景:

用户在页面操作:添加一名学生信息(学生姓名为必填,其余属性均为选填)


  
  1. package com.wisdom.qiqiao;
  2. import lombok.Data;
  3. import java.io.Serializable;
  4. import java.util.List;
  5. /**
  6. * 学生类
  7. */
  8. @Data
  9. public class Student implements Serializable {
  10. /**
  11. * 学生姓名(必填)
  12. */
  13. private String studentName;
  14. /**
  15. * 学生年龄(选填)
  16. */
  17. private Integer studentAge;
  18. /**
  19. * 学生住址(选填)
  20. */
  21. private String studentAddress;
  22. /**
  23. * 兴趣爱好(选填)
  24. */
  25. private List<Hobby> hobbyList;
  26. }
  27. /**
  28. * 兴趣爱好类
  29. */
  30. @Data
  31. class Hobby implements Serializable {
  32. /**
  33. * 兴趣爱好名称(必填)
  34. */
  35. private String hobbyName;
  36. /**
  37. * 兴趣爱好程度(1~10分)(非必填)
  38. */
  39. private Integer hobbyDegree;
  40. }

假设用户将所有属性都进行填写,则前端传给后端的JSON格式如下:


  
  1. {
  2. "studentName": "张三",
  3. "studentAge": 18,
  4. "studentAddress": "广东",
  5. "hobbyList": [
  6. {
  7. "hobbyName": "Java",
  8. "hobbyDegree": 8
  9. },
  10. {
  11. "hobbyName": "前端",
  12. "hobbyDegree": 7
  13. }
  14. ]
  15. }

 

假设用户只填写了“必填项”(即 学生姓名),则前端传给后端JSON格式将存在异议,大致有四种:

格式一:


  
  1. {
  2. "studentName": "张三"
  3. }

格式二:


  
  1. {
  2. "studentName": "张三",
  3. "studentAge": null,
  4. "studentAddress": null,
  5. "hobbyList": null
  6. }

格式三:


  
  1. {
  2. "studentName": "张三",
  3. "studentAge": null,
  4. "studentAddress": null,
  5. "hobbyList": []
  6. }

格式四:


  
  1. {
  2. "studentName": "张三",
  3. "studentAge": "",
  4. "studentAddress": "",
  5. "hobbyList": []
  6. }

 

由于Spring Boot为我们集成了简便的校验注解,所以一般会采用优雅写法接收前端入参:


  
  1. package com.wisdom.qiqiao;
  2. import lombok.Data;
  3. import org.hibernate.validator.constraints.Length;
  4. import org.hibernate.validator.constraints.Range;
  5. import javax.validation.Valid;
  6. import javax.validation.constraints.NotBlank;
  7. import javax.validation.constraints.Size;
  8. import java.io.Serializable;
  9. import java.util.List;
  10. /**
  11. * 学生 保存DTO
  12. *
  13. * @author Jiahai
  14. */
  15. @Data
  16. public class StudentForSaveDTO implements Serializable {
  17. /**
  18. * 学生姓名(必填)
  19. */
  20. @NotBlank(message = "学生姓名 不可为空")
  21. @Length(min = 2, max = 5, message = "学生名字 不合法")
  22. private String studentName;
  23. /**
  24. * 学生年龄(选填)
  25. */
  26. @Range(min = 18, max = 30, message = "学生年龄 不合法")
  27. private Integer studentAge;
  28. /**
  29. * 学生住址(选填)
  30. */
  31. @Length(min = 2, max = 5, message = "学生名字 不合法")
  32. private String studentAddress;
  33. /**
  34. * 兴趣爱好(选填)
  35. */
  36. @Valid
  37. @Size(min = 2, max = 5, message = "兴趣爱好 不合法")
  38. private List<HobbyForSaveDTO> hobbyList;
  39. }
  40. /**
  41. * 兴趣爱好类
  42. */
  43. @Data
  44. class HobbyForSaveDTO implements Serializable {
  45. /**
  46. * 兴趣爱好名称(必填)
  47. */
  48. @Length(min = 2, max = 6, message = "兴趣爱好名称 不合法")
  49. private String hobbyName;
  50. /**
  51. * 兴趣爱好程度(1~10分)(非必填)
  52. */
  53. @Range(min = 1, max = 10, message = "兴趣爱好程度 不合法")
  54. private Integer hobbyDegree;
  55. }

  
  1. package com.wisdom.qiqiao;
  2. import com.wisdom.qiqiao.base.response.APIResult;
  3. import org.springframework.validation.annotation.Validated;
  4. import org.springframework.web.bind.annotation.PostMapping;
  5. import org.springframework.web.bind.annotation.RequestBody;
  6. import org.springframework.web.bind.annotation.RequestMapping;
  7. import org.springframework.web.bind.annotation.RestController;
  8. /**
  9. * 学生 API
  10. *
  11. * @author Jiahai
  12. */
  13. @RestController
  14. @RequestMapping("/student")
  15. public class StudentController {
  16. /**
  17. * 新增
  18. *
  19. * @param studentForSaveDTO 学生 新增入参DTO
  20. * @return
  21. */
  22. @PostMapping
  23. public APIResult save(@Validated @RequestBody StudentForSaveDTO studentForSaveDTO) {
  24. // TODO 保存学生信息...
  25. return APIResult.success();
  26. }
  27. }

 

Spring Boot 为我们提供的校验注解(来自 spring boot starter validation),可以使得后端灵活处理以下场景:

  • 对于必填字段,可要求前端必填(例如 @NotBlank(message = "学生姓名 不可为空")),且对入参值进行指定规则校验(例如 @Length(min = 2, max = 5, message = "学生名字 不合法"))

  • 对于选填字段,可灵活处理 当前端传了值,则可进入指定校验;如果前端不传值,则跳过校验

 

个人看法:

  1. 后端只需要关注前端入参的必要字段(例如,如果后端能根据学生ID获取到学生的teacherId,则在新增学生信息的时候无需前端传teacherId);
  2. 抛开前后端数据接收格式来说,仅从网络带宽而言,前端传无意义的字段给后端,后端并不接收,但会占用更多的服务器带宽进行传输;
  3. 用户并未填写的字段,也以null或者空字符串""传给后端,则上述优雅接收入参方式不可行,后端则需要写很多if-else等做判断;
  4. 后端返回给前端数据也是入参,假设前端只需要studentId与studentName,而后端则返回学生表所有字段,尽管前端可以只接收需要的字段,但是后端从数据库查询消耗性能与带宽、从服务器返回到用户浏览器又消耗一次带宽,没有意义。

 

综上所述:

  1. 最优方案为 格式一;
  2. 前后端分离优势之一是为了将服务器的压力分摊到客户端,前端对数据进行适当处理(如入参校验,不用每次非法字符都给服务器校验),并不消耗服务器资源,多做一步处理并不是什么难事;
  3. 相反,后端也会根据前端所需字段而进行处理再返回,所以前端是否也应该对数据进行适当处理 再与后端交互呢?

 

本文仅为个人观点,欢迎大家发表意见,谢谢。


转载:https://blog.csdn.net/qq_23354917/article/details/111941838
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场