Spring boot开源项目之个人博客(15)—博客详情页面展示
博客详情页分为两个部分,一个是博客详情信息的渲染,一个是评论功能。在这之前,做了一个全局搜索的功能。
1. 全局搜索
在导航栏有一个搜索框,定义搜索的功能是输入字段查找标题和内容中包含字段的博客,然后把符合条件的博客分页展示。
简单处理一下页面,粘贴之前博客列表中的div,就省去了再写theamleaf去渲染了,主要内容是根据搜索字段查找符合条件的博客。从前端开始看
<form action="#" name="search" th:action="@{/search}" method="post">
<div class="ui icon input">
<input type="text" placeholder="search..." name="query" th:value="${query}">
<i onclick="document.search.submit()" class="search link icon"></i>
</div>
</form>
用form包裹搜索框,post方式提交,给icon图标定义点击事件,通过name定位到表单,点击则提交,输入框则向后台推一个query字符串,还需要后端处理完之后,跳转页面再把query传回来并将其值渲染在搜索框里。
后台controller方法
@PostMapping("/search")
public String search(@PageableDefault(size = 6, sort = {
"updateTime"}, direction = Sort.Direction.DESC) Pageable pageable,
Model model, @RequestParam String query){
model.addAttribute("page", blogService.listBlog("%" +query+ "%", pageable));
model.addAttribute("query", query);
return "search";
}
@RequestParam String query
取出前端传来的搜索条件字符串,controller里主要是把查找到的数据推到前端。
service层
@Override
public Page<Blog> listBlog(String query, Pageable pageable) {
return blogRepository.findBlogByQuery(query, pageable);
}
把搜索条件和pageable对象传入dao层,在数据库层面查找数据,因为需要分页,需要返回Page<Blog>
类型,这个要注意,否则前端会取不到值。
dao层
@Query("select b from Blog b where b.title like ?1 or b.content like ?1")
Page<Blog> findBlogByQuery(String query, Pageable pageable);
通过@Query
自定义语句进行条件查找,like ?1
表示对第一个参数进行like查找,另外,sql的like查询的条件字段是有特定格式的:”%内容%“,在字段左右需要加一个“%”,前端传来的是字符串,所以需要进行拼接,这个工作是在controller层完成的。
这样,整个功能就完成了,当输入搜索条件点击搜索时,就会跳转到新的搜索结果页面,博客列表分页展示。
2. 博客内容处理
这部分和列表差不多,先通过id找到对应的博客对象,然后推到前端使用theamleaf渲染页面,需要记录的是博客内容部分的处理。
博客内容在数据库里是以带Markdown语法的文本格式存储的,显然,直接取出来渲染到页面上,格式就全乱了,所以必须把Markdown语法格式的文本转化成HTML格式的文本,而且也不希望改变数据库的内容,因为博客的编辑也需要提取内容,那里是需要Markdown语法格式的。此外,我们有生成目录的插件,表格还希望采用semantic的ui样式,文章中的a标签链接也希望点击之后打开新的页面,这时,就需要一个灵活方便的Markdown转HTML的插件,在对内容格式进行转化之后,还能进行一些定制化操作。
首先,引入依赖
<!--markdown转html插件-->
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-heading-anchor</artifactId>
<version>0.10.0</version>
</dependency>
<dependency>
<groupId>com.atlassian.commonmark</groupId>
<artifactId>commonmark-ext-gfm-tables</artifactId>
<version>0.10.0</version>
</dependency>
第一个是功能的核心,实现Markdown转html,下面两个是为了实现header和table的定制化而导入的。
controller层
@GetMapping("/blog/{id}")
public String blog(@PathVariable Long id, Model model){
model.addAttribute("blog", blogService.getAndInvertBlog(id));
return "blog";
}
根据id查找对应的博客对象,并在service层对博客对象的内容进行处理,再推到前端渲染。
service层
@Override
public Blog getAndInvertBlog(Long id) {
Blog blog = blogRepository.getOne(id);
if (blog == null){
throw new NotFoundException("该博客不存在");
}
Blog b = new Blog();
BeanUtils.copyProperties(blog, b);
String content = b.getContent();
b.setContent(MarkdownUtils.markdownToHtmlExtensions(content));
return b;
}
先根据id取出博客对象,这里将取出的对象的值赋给new出的博客对象,这么做的原因是如果直接对原对象进行set操作会改变数据库的内容。然后定义了一个工具类,将Markdown转化为html,并做一些定制化的扩展功能。
public class MarkdownUtils {
/*
*markdown格式转换成HTML格式
*/
public static String markdownToHtml(String markdown){
Parser parser = Parser.builder().build();
org.commonmark.node.Node docoment = parser.parse(markdown);
HtmlRenderer renderer = HtmlRenderer.