实现博客列表
约定前后端交互接口
-
[请求]
/blog/getList GET
-
[响应]
{"code": 200,"errMsg": null,"data": [{"id": 1,"title": "第一篇博客","content": "111我是博客正文我是博客正文我是博客正文","userId": 1,"updateTime": "2024-08-22 11:27:03"},.....] }
客户端给服务器发送一个
/blog/getlist
这样的 HTTP 请求,服务器给客户端返回了一个 JSON 格式的数据。
实现服务器代码
@JsonFormat
@Data
public class BlogInfoResponse {private Integer id;private String title;private String content;private Integer userId;@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")private LocalDateTime updateTime;
}
补充说明:
LocalDate
、LocalDataTime
都类似于时间工具类 Data
,只是会分出时区
,前者是具体的年月日,后者则是年月日时分秒
通过 @JsonFormat
设置页面返回的日期格式
,格式参考 Overview (Java Platform SE 8 )。
SimpleDateFormat
可以搭配 Data 对象一起使用,可以按照我们配置的格式,输出当前时间,优化 Data 的可读性:
Controller
@RequestMapping("/blog")
@RestController
public class BlogController {@Autowiredprivate BlogService blogService;@RequestMapping("/getList")public List<BlogInfoResponse> getList() {return blogService.getList();}
}
使用 SOA 架构改进 Service
Impl
基于 SOA 理念,Service 采用接口对外提供服务
,实现类用 Impl
的后缀与接口区别
。
SOA(Service-Oriented Architecture,面向服务的架构)是一种高层级的架构设计理念,可通过在网络中使用基于通用通信语言的服务接口,让软件组件可重复使用。
接口测试:
优化返回响应
在上面的测试中,我们发现给前端返回的信息有 deleteFlag
,并且 createTime
、updateTime
只需要在页面上显示一个即可,所以我们还需要对后端返回响应再进一步处理:
纠正:下图中的 BlogReponse 改为 BlogInfoReponse
修改 BlogService 接口返回值的泛型 :
修改实现类 BlogServiceImpl 中,重写方法 getList() 的返回值:
因为在 mapper 层,泛型已经锁定了 BlogInfo
这个实体类:
调用条件构造器, 并执行完对应 SQL 语句后,因为 Mapper 中 extends BaseMapper<BlogInfo>
, List<BlogInfo>
不能修改:
遍历集合对象方法 stream( ).map
在 BlogServiceImpl
的 getList()
方法中,需将 Mapper
返回的 List<BlogInfo>
类型,转换为 List<BlogInfoResponse>
类型,再返回给 Controller ;转换操作可用 for 循环
或 lambda
表达式实现:
使用 lamda
表达式遍历 blogInfos
中的每一个元素,然后在 { }
中将 BlogInfo
类型转为BlogInfoResponse
使用工具类 BeanUtils 内置方法拷贝源实体类属性值
我们可以使用 setter() 对 blogInfoReponse 的每个属性赋值,也可以使用 Spring 提供的工具类 BeanUtil
,并且调用这个工具类的内置方法 copyProperties
使用collect(Collectors.toList( ))将遍历元素存入集合
又因为 getList() 方法的返回值为 List<BlogInfoReponse>
,所以我们要把 lamda 表达式返回的每一个 blogInfoReponse
存入一个集合中:
如上图,调用的是 collect(Collectors.toList())
方法,将转换好的对象放入集合中,并接收,接收后将集合返回:
接口测试
重新测试接口:
在 BlogInfoReponse
中加上 @Data
注解后,重新测试接口,此时前端拿到的响应,就没有 delete_flag
和 updateTime
字段了:
public interface BlogService {List<BlogInfoResponse> getList();
}
@Service
public class BlogServiceImpl implements BlogService {@Autowiredprivate BlogInfoMapper blogInfoMapper;@Overridepublic List<BlogInfoReponse> getList() {QueryWrapper<BlogInfo> queryWrapper = new QueryWrapper<>();queryWrapper.lambda().eq(BlogInfo::getDeleteFlag, 0);List<BlogInfo> blogInfos = blogInfoMapper.selectList(queryWrapper);List<BlogInfoReponse> blogInfoReponses = blogInfos.stream().map(blogInfo -> {BlogInfoReponse blogInfoReponse = new BlogInfoReponse();BeanUtils.copyProperties(blogInfo, blogInfoReponse);return blogInfoReponse;}).collect(Collectors.toList());return blogInfoReponses;}
}
部署程序,验证服务器是否能正确返回数据,使用 URL http://127.0.0.1:8080/blog/getList 即可。
实现客户端代码
修改 blog_list.html
,删除之前写死的博客内容(即 <div class="blog">
),并新增 js 代码处理 ajax 请求。
- 使用 ajax 给服务器发送 HTTP 请求。
- 服务器返回的响应是一个 JSON 格式的数据,根据这个响应数据使用 DOM API 构造页面内容。
- 跳转到博客详情页的 url 形如
blog_detail.html?blogId=1
,这样就可以让博客详情页知道当前是要访问哪篇博客。
$.ajax({type: "get",url: "/blog/getlist",success: function (result) {if (result.code == 200 && result.data != null && result.data.length > 0) {// 循环拼接数据到 documentvar finalHtml = "";for (var blog of result.data) {finalHtml += '<div class="blog">';finalHtml += '<div class="title">' + blog.title + '</div>';finalHtml += '<div class="date">' + blog.createTime + '</div>';finalHtml += '<div class="desc">' + blog.content + '</div>';finalHtml += '<a class="detail" href="blog_detail.html?blogId=' + blog.id + '">查看全文>></a>';finalHtml += '</div>';}$(".right").html(finalHtml);}}
});
通过 URL http://127.0.0.1:8080/blog_list.html 访问服务器,验证效果。