> 文章列表 > 博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

✏️作者:银河罐头
📋系列专栏:JavaEE

🌲“种一棵树最好的时间是十年前,其次是现在”

目录

  • 准备工作
  • 数据库设计
    • 表设计
  • 封装数据库的连接操作
  • 创建实体类
  • 封装数据库的增删改查
  • 实现博客列表页
    • 约定前后端交互接口
    • 开发后端代码
    • 开发前端代码
  • 实现博客详情页
    • 约定前后端交互接口
    • 开发后端代码
    • 开发前端代码
  • 实现博客登录页
    • 约定前后端交互接口
    • 开发前端代码
    • 开发后端代码
  • 实现强制要求登陆
    • 约定前后端交互接口
    • 开发后端代码
    • 开发前端代码
  • 实现显示用户信息
    • 约定前后端交互接口
    • 列表页
    • 详情页
  • 退出登录状态
    • 约定前后端交互接口
    • 开发后端代码
    • 开发前端代码
  • 发布博客
    • 约定前后端交互接口
    • 开发后端代码
    • 开发前端代码

准备工作

创建项目,引入依赖,把之前写的前端页面拷贝进去。

依赖主要是:servlet, mysql, jackson

博客系统(前后端分离)

数据库设计

表设计

在当前的博客系统中,主要涉及到 2 个实体,博客 和 用户。

创建 2 张表,来表示博客和用户。用户和博客之间的关系是一对多。

博客系统(前后端分离)

把一些基本的数据库操作先封装好,以备后用。

-- 这个文件主要写建库建表语句
-- 建议 在建表的时候把 sql 语句保留下来,以便后续部署其他机器的时候就方便了。create database if not exists java_blog_system;
use java_blog_system;
-- 删除旧表,重新创建新表
drop table if exists user;
drop table if exists blog;-- 真正创建表
create table blog(blogId int primary key auto_increment,title varchar(128),content varchar(4096),postTime datetime,useId int
);create table user(userId int primary key auto_increment,username varchar(20) unique, -- 要求用户名和别人不重复password varchar(20)
);

封装数据库的连接操作

public class DBUtil {private static DataSource dataSource = new MysqlDataSource();static {((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/java_blog_system?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("123456");}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {if(resultSet != null){try {resultSet.close();} catch (SQLException e) {e.printStackTrace();}}if(statement != null){try {statement.close();} catch (SQLException e) {e.printStackTrace();}}if(connection != null){try {connection.close();} catch (SQLException e) {e.printStackTrace();}}}
}

创建实体类

实体类,和表中记录对应的类。

blog 表,Blog 类对应。Blog 的一个对象,就对应表中的一条记录。

user 表,User 类对应。User 的一个对象,就对应表中的一条记录。

实体类里要有哪些属性,是和表里的列是密切相关的。

public class Blog {private int blogId;private String title;private String content;private Timestamp postTime;private int userId;public int getBlogId() {return blogId;}public void setBlogId(int blogId) {this.blogId = blogId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Timestamp getPostTime() {return postTime;}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}
}
public class User {private int userId;private String username;private String password;public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}

封装数据库的增删改查

针对博客表,创建 BlogDao

针对用户表,创建 UserDao

提供一些方法,来进行增删改查。

Dao, Data Access Object 访问数据的对象

//通过这个类,封装对博客表的基本操作
public class BlogDao {//1.新增一个博客public void add(Blog blog) {}//2.根据 博客 id 来查询博客(博客详情页中)public Blog selectById(int blogId){return null;}//3.查询出数据库中所有的博客列表(博客列表页)public List<Blog> selectAll(){return null;}//4.删除指定博客public void delete(int blogId){}
}

博客系统(前后端分离)

//通过这个类,封装对博客表的基本操作
public class BlogDao {//1.新增一个博客public void add(Blog blog) {Connection connection = null;PreparedStatement statement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造 sqlString sql = "insert into blog values(null, ?, ?, ?, ?)";statement = connection.prepareStatement(sql);statement.setString(1, blog.getTitle());statement.setString(2,blog.getContent());statement.setTimestamp(3,blog.getPostTime());statement.setInt(4,blog.getUserId());//3.执行 sqlstatement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {//4.断开连接,释放资源DBUtil.close(connection, statement, null);}}//2.根据 博客 id 来查询博客(博客详情页中)public Blog selectById(int blogId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.和数据库建立连接connection = DBUtil.getConnection();//2.构造 SQLString sql = "select * from blog while blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);//3.执行 SQLresultSet = statement.executeQuery();//4.遍历结果集合//blogId 是自增主键,是唯一的。所以要么是没有查到,要么是查到了一条记录//此处可以不使用 where, 直接 if 判定即可if (resultSet.next()){Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));return blog;}} catch (SQLException e) {e.printStackTrace();}finally {//5.释放资源,断开连接DBUtil.close(connection,statement,resultSet);}return null;}//3.查询出数据库中所有的博客列表(博客列表页)public List<Blog> selectAll(){List<Blog> blogs = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造 SQL 语句String sql = "select * from blog";statement = connection.prepareStatement(sql);//3.执行 sql 语句statement.executeQuery();//4.遍历结果集合while(resultSet.next()){Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));//注意这里的正文,博客列表页不需要把整个正文都显示出来String content = resultSet.getString("content");if(content.length() >= 100){content = content.substring(0,100) + "...";}blog.setContent(content);blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));blogs.add(blog);}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection, statement, resultSet);}return blogs;}//4.删除指定博客public void delete(int blogId){Connection connection = null;PreparedStatement statement = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构建 SQL 语句String sql = "delete from blog while blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);//3.执行 sqlstatement.executeUpdate();} catch (SQLException e) {e.printStackTrace();}finally {//4.关闭连接DBUtil.close(connection,statement,null);}}
}
//针对用户表提供的基本操作
//由于没写注册功能,此处就不写 add 了
//也没有用户删号这个功能,也就不必 delete
public class UserDao {//根据 userId 来查用户信息public User selectById(int userId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造 SQLString sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,userId);//3.执行 sqlresultSet = statement.executeQuery();//4.遍历结果集if(resultSet.next()){User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}//根据 username 来查用户信息(登录的时候)public User selectByUsername(String username){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.建立连接connection = DBUtil.getConnection();//2.构造 SQLString sql = "select * from user where username = ?";statement = connection.prepareStatement(sql);statement.setString(1,username);//3.执行 sqlresultSet = statement.executeQuery();//4.遍历结果集if(resultSet.next()){User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {e.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}
}

实现博客列表页

当前博客列表页上的数据都是写死的,正确的做法应该是从服务器获取数据并显示到页面上。

让博客列表页,加载的时候,通过 ajax 给服务器发一个请求,服务器查数据库获取博客列表数据,返回给浏览器,浏览器根据数据构造页面内容。

这样的交互过程,称为 “前后端分离”。

前端只是向后端索要数据,而不是请求具体的页面。后端也仅仅是返回数据。

这样设定的目的就是让前端和后端更加解耦。

由浏览器进行具体的页面渲染,减少了服务器的工作量。

上述是实现博客列表页的基本思路。

接下来需要:

1.约定前后端交互接口

获取博客列表功能,前端要发啥请求,后端要返回啥响应。

2.开发后端代码

3.开发前端代码

约定前后端交互接口

博客系统(前后端分离)

开发后端代码

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();List<Blog> blogs = blogDao.selectAll();//需要 把 blogs 转成符合要求的 json 字符串String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}

博客系统(前后端分离)

把 servlet 放到 api 包里,api 不一定非得是 类/方法,也可以是 “网络请求”(处理 http 请求,返回 http 响应)

开发前端代码

在博客列表页加载过程中,通过 ajax 访问服务器数据,再把拿到的数据构造到页面中。

博客系统(前后端分离)

<script src="./js/jquery.min.js"></script><script>//在页面加载的时候,向服务器发起请求,获取博客列表数据function getBlogs() {$.ajax({type:'get',url: 'blog',success: function(body) {//响应的正文是 json 字符串,已经被 jquery 自动解析成 js 对象数组了// for 循环遍历即可let containerRight = document.querySelector('.container-right');for(let blog of body){//构造页面内容,参考之前写好的 html 代码//构造整个博客 divlet blogDiv = document.createElement('div');blogDiv.className = 'blog';//构造 标题let titleDiv = document.createElement('div');titleDiv.className = 'title';titleDiv.innerHTML = blog.title;blogDiv.appendChild(titleDiv);//构造发布时间let dateDiv = document.createElement('div');dateDiv.className = 'date';dateDiv.innerHTML = blog.postTime;blogDiv.appendChild(dateDiv);//构造博客摘要let descDiv = document.createElement('div');descDiv.className = 'desc';descDiv.innerHTML = blog.content;blogDiv.appendChild(descDiv);//构造查看全文按钮let a = document.createElement('a');a.innerHTML = '查看全文 &gt;&gt;';//希望点击之后能跳转到博客详情页//为了让博客详情页知道是点了哪个博客,把 blogId 传过去a.href = 'blog_detail.html?blogId=' + blog.blogId;blogDiv.appendChild(a);//把 blogDiv 加到父元素中containerRight.appendChild(blogDiv);}}});}//记得调用函数getBlogs();</script>

此时,博客列表页实现完成。

博客系统(前后端分离)

此时看的的博客列表页是空着的,因为博客列表的数据来自于数据库,而现在数据库是空着的。

博客系统(前后端分离)

-- 构造测试数据
insert into blog values(1, '这是我的第一篇博客', '从今天开始我要认真敲代码', now(),1);
insert into blog values(2, '这是我的第二篇博客', '从昨天开始我要认真敲代码', now(),1);
insert into blog values(3, '这是我的第三篇博客', '从前天开始我要认真敲代码', now(),1);

此处只是把 idea 当做是个 记事本,来记录下 sql 而已。真正要执行,要复制到 sql 客户端里执行。

博客系统(前后端分离)

博客系统(前后端分离)

有 2 个 bug,

1.不应该显示时间戳,而应该显示格式化时间

需要用到一个 格式化时间的类,转换一下。

SimpleDateFormat

博客系统(前后端分离)

public Timestamp getPostTimestamp() {return postTime;
}public String getPostTime(){//把时间戳转换成格式化时间SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");return simpleDateFormat.format(postTime);
}

2.顺序,新的博客在上面,老的博客在下面。

博客系统(前后端分离)

博客系统(前后端分离)

实现博客详情页

接下来实现博客详情页,点击"查看全文"按钮,就能跳转到博客详情页中。跳转之后,在博客详情页中,对服务器发起 ajax 请求,从服务器获取数据,然后显示出来。

博客系统(前后端分离)

约定前后端交互接口

博客系统(前后端分离)

开发后端代码

博客系统(前后端分离)

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();//尝试获取下 query string 中的 blogId字段String blogId = req.getParameter("blogId");if (blogId == null) {//说明 query string 不存在,说明这次请求是在获取博客列表页List<Blog> blogs = blogDao.selectAll();//需要 把 blogs 转成符合要求的 json 字符串String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);} else {//说明 query string 存在,说明这次请求是在获取博客详情页Blog blog = blogDao.selectById(Integer.parseInt(blogId));if(blog == null) {System.out.println("当前 blogId = " + blogId + " 对应的博客不存在!");}String respJson = objectMapper.writeValueAsString(blog);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}}
}

开发前端代码

在 blog_datail.html 中,加入 ajax,来获取数据。

博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

<script src="js/jquery.min.js"></script>
<!-- 保证 这几个 js 的加载要在 jquery 之后,因为 edit.md 依赖 jquery-->
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
<script>$.ajax({type: 'get',url: 'blog' + location.search,success: function(body) {//回调函数//处理响应结果,此处的 body 就表示一个博客的 js 对象//1.更新标题let titleDiv = document.querySelector('.title');titleDiv.innerHTML = body.title;//2.更新日期let dateDiv = document.querySelector('.date');dateDiv.innerHTML = body.postTime;//3.更新博客正文//此处不应该直接把博客内容填充到标签里editormd.markdownToHTML('content',{ markdown: blog.content });}
});
</script>

博客系统(前后端分离)

代码改完之后,重新启动服务器,发现此时博客详情页的结果还是之前未修改的状态。

博客系统(前后端分离)

始端用户访问加速节点时,如果该节点有缓存住了要被访问的数据时就叫做命中,如果没有的话需要回原服务器取,就是没有命中。

博客系统(前后端分离)

缓冲问题解决了,但是页面仍然不是预期的效果。

博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

修改上述代码之后,发现正文有了,但是标题没出来。

就需要抓包,看下服务器返回结果是否符合预期,就可以确定是前端问题还是后端问题了。

博客系统(前后端分离)

响应结果是正确的,后端代码应该没问题。

接下来检查前端代码。

博客系统(前后端分离)

解决方案,把选择器写的更准确一些。

博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

博客系统(前后端分离)

实现博客登录页

博客系统(前后端分离)

此处输入用户名,密码,点击登录,就会触发一个 http 请求。服务器验证用户名和密码,如果登陆成功,就跳转到博客列表页。

当前还只是一个 输入框,还不能提交请求,需要给改成 form 表单。

约定前后端交互接口

博客系统(前后端分离)

开发前端代码

在页面里加上 form 表单,使点击登录操作能 触发请求。

<!-- 垂直水平居中的登录对话框 -->
<div class="login-dialog"><form action="login" method="post"><h3>登录</h3><div class="row"><span>用户名</span><input type="text" id="username" placeholder="手机号/邮箱" name="username"></div><div class="row"><span>密码</span><input type="password" id="password" name="password"></div><div class="row"><input type="submit" id="submit" value="登录"></div></form>
</div>

博客系统(前后端分离)

博客系统(前后端分离)

请求已经构造出来了。

开发后端代码

此处需要价格 servlet 来处理 登录请求。

@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置请求的编码,告诉 servlet 按照啥格式理解请求req.setCharacterEncoding("utf8");//设置响应的编码,告诉 servlet 以啥样的格式构造响应
//        resp.setCharacterEncoding("utf8");resp.setContentType("text/html;charset=utf8");//1.读取参数中的用户名,密码//注意,如果用户名密码中存在中文,这里读取可能会乱码String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || "".equals(username) || password == null || "".equals(password)){//登陆失败!String html = "<h3> 登录失败! 缺少 username 或者 password 字段 </h3>";resp.getWriter().write(html);return;}//2.读一下数据库,看用户名是否存在,密码是否匹配UserDao userDao = new UserDao();User user = userDao.selectByUsername(username);if(user == null){//用户不存在String html = "<h3> 登录失败! 用户名或密码错误 </h3>";resp.getWriter().write(html);return;}if(!password.equals(user.getPassword())){//密码错误String html = "<h3> 登录失败! 用户名或密码错误 </h3>";resp.getWriter().write(html);return;}//3.用户名密码验证通过,登陆成功,接下来创建会话,使用该会话保存用户信息HttpSession session = req.getSession(true);session.setAttribute("username", username);//4.进行重定向,跳转到博客列表页resp.sendRedirect("blog_list.html");}
}

这样还是无法登录成功,因为数据库没有保存用户名密码。

博客系统(前后端分离)

构造一些测试数据,保存到数据库里。

博客系统(前后端分离)

博客系统(前后端分离)

登陆成功。

实现强制要求登陆

当用户访问博客列表页/详情页/编辑页,要求用户必须是已登录的状态,如果用户还没有登录,就会强制跳转到登录页面。

博客系统(前后端分离)

约定前后端交互接口

博客系统(前后端分离)

开发后端代码

博客系统(前后端分离)

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf8");//使用这个方法来获取用户的登录状态HttpSession session = req.getSession(false);if(session == null){User user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}User user = (User)session.getAttribute("user");if(user == null){user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}//确实取出了 user 对象,直接返回即可String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);
}

开发前端代码

博客系统(前后端分离)

function checkLogin() {$.ajax({type: 'get',url: 'login',success: function(body) {if(body.userId && body.userId > 0){//登陆成功console.log("当前用户已登录!");} else {//当前未登录,强制跳转到登录页location.assign('blog_login.html');}}});
}
checkLogin();

把这段代码加到 列表页,详情页,编辑页的代码中。

重启服务器之后,之前的登录状态就没了。

博客系统(前后端分离)

实现显示用户信息

目前页面的用户信息部分是写死的.

博客系统(前后端分离)

这个地方时写死的,希望能够动态生成。

1.如果是博客列表页,此处显示登录用户的信息。

2.如果是博客详情页,此处显示该文章的作者。

比如我以"张三"的身份登录,查看博客列表页时,左侧应该显示"张三"的用户信息,然后我点开李四写的博客,跳转到博客详情页,应该显示作者"李四"的信息。

约定前后端交互接口

博客系统(前后端分离)

列表页

function checkLogin() {$.ajax({type: 'get',url: 'login',success: function(body) {if(body.userId && body.userId > 0){//登陆成功console.log("当前用户已登录!");//把当前用户的名字显示到界面上let h3 = document.querySelector('.container-left .card h3');h3.innerHTML = body.username;} else {//当前未登录,强制跳转到登录页location.assign('blog_login.html');}}});
}

博客系统(前后端分离)

详情页

@WebServlet("/author")
public class AuthorServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String blogId = req.getParameter("blogId");if(blogId == null){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("参数非法,缺少 blogId");return;}//根据 blogId 查询 Blog 对象BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectById(Integer.parseInt(blogId));if(blog == null){//博客不存在resp.setContentType("text/html; charset=utf8");resp.getWriter().write("没有找到指定博客: blogId = " + blogId);return;}//根据 Blog 中的 userId 找到 用户信息UserDao userDao = new UserDao();User author = userDao.selectById(blog.getUserId());String respJson = objectMapper.writeValueAsString(author);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}
}
function getAuthor() {$.ajax({type: 'get',url: 'author',success: function(body) {//把 username 设置到界面上let h3 = document.querySelector('.container-left .card h3');h3.innerHTML = body.username;}});
}
getAuthor();

博客系统(前后端分离)

博客系统(前后端分离)

抓包后发现是少了 query string.

改下前端代码。

function getAuthor() {$.ajax({type: 'get',url: 'author' + location.search,success: function(body) {//把 username 设置到界面上let h3 = document.querySelector('.container-left .card h3');h3.innerHTML = body.username;}});
}
getAuthor();

博客系统(前后端分离)

退出登录状态

注销(sign out)

判定登录状态:

1.看是否能查到 http session 对象;

2.看 session 对象里有没有 user 对象

博客系统(前后端分离)

实现退出登录,要么把 session 干掉,要么把 user 干掉。只要干掉一个就可以。

HttpSession 对象要想干掉,麻烦点。getSession 能够创建/获取会话,没有删除会话的方法。直接删除还不好删,可以通过设置会话的过期时间来达到类似的效果,但并不优雅。

更好的办法是把 user 干掉,通过 removeAttribute 就删了。

之前的逻辑中, httpSession 和 user 是一荣俱荣一损俱损的情况,但是引入注销逻辑后,就出现有 httpSession 没 user 的情况。

明确思路之后,先设计前后端交互接口。

约定前后端交互接口

博客系统(前后端分离)

博客系统(前后端分离)

开发后端代码

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession httpSession = req.getSession(false);if(httpSession == null){//未登录,提示出错resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录!");return;}httpSession.removeAttribute("user");resp.sendRedirect("login.html");}
}

开发前端代码

把 blog_detail.html, blog_edit.html, blog_list.html 这几个的 a 标签改下就行。

博客系统(前后端分离)

发布博客

博客系统(前后端分离)

写博客写了一些内容,点击"发布文章",没有反应。

约定前后端交互接口

博客系统(前后端分离)

开发后端代码

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();//尝试获取下 query string 中的 blogId字段String blogId = req.getParameter("blogId");if (blogId == null) {//说明 query string 不存在,说明这次请求是在获取博客列表页List<Blog> blogs = blogDao.selectAll();//需要 把 blogs 转成符合要求的 json 字符串String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);} else {//说明 query string 存在,说明这次请求是在获取博客详情页Blog blog = blogDao.selectById(Integer.parseInt(blogId));if(blog == null) {System.out.println("当前 blogId = " + blogId + " 对应的博客不存在!");}String respJson = objectMapper.writeValueAsString(blog);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 发布博客//读取请求,构造 blog 对象,插入数据库中HttpSession httpSession = req.getSession(false);if(httpSession == null){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录,无法发布博客!");return;}User user = (User) httpSession.getAttribute("user");if(user == null){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录,无法发布博客!");return;}//确保登陆之后,就可以把作者拿到了//获取博客标题和正文String title = req.getParameter("title");String content = req.getParameter("content");if(title == null || "".equals(title) || content == null || "".equals(content)){resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前提交数据有误,标题或正文为空!");return;}//构造一个 blog 对象Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);blog.setUserId(user.getUserId());//发布时间,在 java 中生成 / 在数据库中生成都行blog.setPostTime(new Timestamp(System.currentTimeMillis()));//插入数据库BlogDao blogDao = new BlogDao();blogDao.add(blog);//跳转到博客列表页resp.sendRedirect("blog_list.html");}
}

开发前端代码

把页面改造下,能够构造出请求。

博客系统(前后端分离)

<div class="blog-edit-container"><form action="blog" method="post"><!-- 博客标题编辑区 --><div class="title"><input type="text" id="title" placeholder="输入文章标题" name="title"><input type="submit" id="submit" value="发布文章"></div><!-- 博客编辑器, 这里用 id 是为了和 markdown 编辑器对接 --><div id="editor"><textarea name="content" style="display: none;"></textarea></div></form>
</div>

博客系统(前后端分离)

代码改完之后,再次运行,发现只显示一小段了。

前端代码有问题,看 chrome 开发者工具。

博客系统(前后端分离)

之前给编辑器设置的高度。

博客系统(前后端分离)

<form action="blog" method="post" style="height: 100%;">

博客系统(前后端分离)

发现乱码了?!

乱码是提交博客的时候乱的,还是获取博客的时候乱的?

这里大概率是提交的时候乱的,因为获取数据这个功能前面已经测试过了,而提交这个功能还没有测试过。

只要看下数据库是不是乱的。

博客系统(前后端分离)

应该是提交的时候就乱了。

把乱码的第4条删掉。

博客系统(前后端分离)

//获取博客标题和正文
req.setCharacterEncoding("utf8");
String title = req.getParameter("title");
String content = req.getParameter("content");

博客系统(前后端分离)