博客系统(使用前后端分离)
前面的代码中我们基于模板的方式来开发了博客系统.
在基于模板方式中, 主要是通过服务器把数据渲染到页面中, 然后直接返回完整的页面给浏览器.
目前现在更主流的开发方式是 “前后端分离” 的方式. 这种方式下服务器端不关注页面的内容, 而只是给 网页端提供数据.
网页端通过 ajax 的方式和服务器之间交互数据, 网页拿到数据之后再根据数据的内容渲染到页面上.
准备工作
-
创建 web 项目
-
创建目录结构
- 配置 pom.xml
前后端分离的方式不需要使用 Thymeleaf 了.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>博客系统(前后端分离)</artifactId>
<version>1.0-SNAPSHOT</version>
<!-- 指定属性信息 -->
<properties>
<encoding>UTF-8</encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- 加入 servlet 依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<!-- servlet 版本和 tomcat 版本有对应关系,切记 -->
<version>3.1.0</version>
<!-- 这个意思是我们只在开发阶段需要这个依赖,部署到 tomcat 上时就不需要了 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.45</version>
</dependency>
<!--
https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -
->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
</dependencies>
<packaging>war</packaging>
<build>
<!-- 指定最终 war 包的名称 -->
<finalName>BlogSystem</finalName>
</build>
</project>
数据库设计
同之前的 博客系统(使用模板技术)
包括:
- 表结构 (文章表, 用户表)
- DBUtil 类
- Blog 类, User 类
- BlogDao 类, UserDao 类
准备前端页面
拷贝页面
把之前写好的博客系统的静态页面拷贝到 webapp 目录中.
此处不需要 templates 目录了.
封装 ajax
在前后端交互中我们需要用到 ajax 进行数据交互.
我们把之前写过的 ajax 函数拷贝过来, 放到一个单独的 js 文件中, 方便后续使用. 创建 js/common.js
// 参数 args 是一个 JS 对象, 里面包含了以下属性
// method: 请求方法
// url: 请求路径
// body: 请求的正文数据
// contentType: 请求正文的格式
// callback: 处理响应的回调函数, 有两个参数, 响应正文和响应的状态码
function ajax(args) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
// 0: 请求未初始化
// 1: 服务器连接已建立
// 2: 请求已接收
// 3: 请求处理中
// 4: 请求已完成,且响应已就绪
if (xhr.readyState == 4) {
args.callback(xhr.responseText, xhr.status)
}
}
xhr.open(args.method, args.url);
if (args.contentType) {
xhr.setRequestHeader('Content-type', args.contentType);
}
if (args.body) {
xhr.send(args.body);
} else {
xhr.send();
}
}
实现博客列表
约定前后端交互接口
[请求]
GET /blog
[响应]
[
{
blogId: 1,
title: "第一篇博客",
content: "博客正文",
userId: 1,
postTime: "2021-07-07 12:00:00"
},
{
blogId: 2,
title: "第二篇博客",
content: "博客正文",
userId: 1,
postTime: "2021-07-07 12:10:00"
},
...
]
我们约定, 浏览器给服务器发送一个 GET /blog
这样的 HTTP 请求, 服务器给浏览器返回了一个 JSON格式的数据.
实现服务器代码
创建 BlogServlet , 放到 api 包中.
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
}
}
实现 doGet, 完成读取博客列表的功能.
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
ServletException, IOException {
resp.setContentType("application/json; charset=utf-8");
BlogDao blogDao = new BlogDao();
List<Blog> blogs = blogDao.selectAll();
String jsonString = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(jsonString);
}
部署程序, 验证服务器是否能正确返回数据 (使用 URLhttp://127.0.0.1:8080/BlogSystem/blog
即可).
实现客户端代码
修改 blog_list.html, 删除之前写死的博客内容(即 <div class="blog">
), 并新增 js 代码处理 ajax 请求.
- 使用 ajax 给服务器发送 HTTP 请求.
- 服务器返回的响应是一个 JSON 格式的数据, 根据这个响应数据使用 DOM API 构造页面内容.
- 响应中的 postTime 字段为 ms 级时间戳, 需要转成格式化日期.
- 列表页中拿到的 “content” 字段其实是已经裁剪过的摘要.
跳转到博客详情页的 url 形如blog_content.html?blogId=1
这样就可以让博客详情页知道当前是要访问哪篇博客.
ajax({
url: 'blog',
method: 'GET',
callback: function (data, status) {
if (status == 200) {
var blogs = JSON.parse(data);
buildBlogs(blogs)
} else {
console.log("status error! " + status);
}
}
})