Java-博客系统(前后端交互)

目录

前言

博客系统基本情况

1 创建项目,引入依赖

2 数据库设计 

2.1 分析

2.2 建库建表

3 封装数据库 

3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装

3.2 在java目录下创建实体类(博客类Blog)

3.2 在java目录下创建实体类(用户类User)

3.3  在java目录下创建BlogDao类

3.4 在java目录下创建UserDao类 

4 获取博客列表页

4.1 约定前后端交互接口

4.2 让前端给服务器发起请求

4.3 服务器处理上述请求,返回响应数据

4.4 让前端处理上述响应数据

5 获取博客详情页 

 5.1 约定前后端交互接口

5.2 让前端给服务器发起请求 

5.3 服务器处理上述请求,返回响应数据

5.4 让前端处理上述响应数据

6 实现登录

6.1 分析

 6.2 约定前后端交互接口

6.3 前端发起请求 

6.4 服务器处理响应

7 实现强制登录

7.1 约定前后端交互接口

7.2 让前端给服务器发起请求

7.3 服务器处理上述请求,返回响应

8 实现显示用户信息

8.1 约定前后端交互接口

 8.2 让前端给服务器发起请求

8.3 服务器处理上述请求,返回响应

8.4 让前端处理上述响应数据

9 实现退出功能

9.1 约定前后端交互接口

9.2  让前端给服务器发起请求

9.3 服务器处理上述请求,返回响应

10 实现发布博客

10.1 约定前后端交互接口

10.2 让前端给服务器发起请求

10.3 服务器处理上述请求,返回响应

前言

基于前后端交互实现一个博客系统


博客系统基本情况

这里实现的博客系统主要分为四个页面,博客列表页、博客详情页、博客编辑页、博客登录页,主要实现的功能有:实现博客列表页的展示功能,实现博客详情页的展示功能、登录功能、强制用户登录后查看信息、显示用户信息、实现注销(退出登录)、发布博客、删除博客。


1 创建项目,引入依赖

<?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>blog_system</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</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.15.0</version></dependency></dependencies></project>

2 数据库设计 

2.1 分析

创建一个数据库,两张表

1)博客表:blog(blogId,title,content,postTime,userld)

2)用户表:user(userId,username,password)

2.2 建库建表

create database if not exists blog_system charset utf8;use blog_system;drop table if exists blog;
drop table if exists user;create table blog (blogId int primary key auto_increment,title varchar(1024),content varchar(4096),postTime datetime,userId int
);create table user(userId int primary key auto_increment,username varchar(50) unique,password varchar(50)
);-- 插入一些数据,方便后续进行测试
insert into blog values(1, '这是第一篇博客', '#今天开始写代码', now(), 1);
insert into blog values(2, '这是第二篇博客', '#今天开始写代码', now(), 1);
insert into blog values(3, '这是第三篇博客', '#今天开始写代码', now(), 1);insert into user values(1, 'zhangsan', '123');
insert into user values(2, 'lisi', '456');

3 封装数据库 

由于这个博客系统要给多个用户使用,因此对数据库进行封装

3.1 在java目录下创建DBUtil类,通过这个类对数据库进行封装

public class DBUtil {private volatile static DataSource dataSource = null;private static DataSource getDataSource() {if (dataSource == null) { //这个if表示判断是否加锁synchronized (DBUtil.class) {if (dataSource == null) { //这个if确保有一个线程把dataSource给new出来dataSource = new MysqlDataSource();((MysqlDataSource) dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/blog_system?characterEncoding=utf8&useSSl=false");((MysqlDataSource) dataSource).setUser("root");((MysqlDataSource) dataSource).setPassword("22222");}}}return dataSource;}//建立连接public static Connection getConnection() throws SQLException {return getDataSource().getConnection();}//关闭连接public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {if (resultSet != null) {try {resultSet.close();} catch (SQLException e) {throw new RuntimeException(e);}}if (statement != null) {try {statement.close();} catch (SQLException e) {throw new RuntimeException(e);}}if (connection != null) {try {connection.close();} catch (SQLException e) {throw new RuntimeException(e);}}}
}

1.上述代码使用try、catch而不用throws来处理异常是因为,如果使用throws,一旦哪一个发生了异常,就有可能存在后面的还有需要释放的资源没释放掉的情况。

2.释放顺序和创建的顺序是相反的。

3.如果不判断是否为空直接close,就会对所有的资源close,有些时候ResultSet就没有创建,直接close,就会导致null异常,所以在close之前要判断一下是否为空。

3.2 在java目录下创建实体类(博客类Blog)

//blog对应博客表里的属性
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 String getPostTime() {//这里修改一下返回值的类型,让最终得到的时间是一个格式化时间//使用Java标准库里的SimpleDateFormat类,来完成时间戳到格式化时间的转换SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}@Overridepublic String toString() {return "Blog{" +"blogId=" + blogId +", title='" + title + '\'' +", content='" + content + '\'' +", postTime=" + postTime +", userId=" + userId +'}';}
}

3.2 在java目录下创建实体类(用户类User)

//User对应用户表里的属性
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;}@Overridepublic String toString() {return "User{" +"userId=" + userId +", username='" + username + '\'' +", password='" + password + '\'' +'}';}
}

3.3  在java目录下创建BlogDao类

//通过这个类来完成针对blog表的操作
public class BlogDao {//1.新增操作Connection connection = null;PreparedStatement statement = null;public void insert(Blog blog) {try {//1.建立连接connection = DBUtil.getConnection();//2.构造数据库String sql = "insert into blog values(null,?,?,now(),?)";statement = connection.prepareStatement(sql);statement.setString(1,blog.getTitle());statement.setString(2,blog.getContent());statement.setInt(3,blog.getUserId());//3.执行SQLstatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,null);}}//2.查询博客列表public List<Blog> getBlogs() {List<Blog> blogList = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();//这里采用降序是为了后续新增的博客在第一个显示String sql = "select * from blog order by postTime desc";statement = connection.prepareStatement(sql);resultSet = statement.executeQuery();while (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));//此处读到正文是整个文章,在博客列表希望显示一笑部分//此处需要对content做一个简单的截断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"));blogList.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,resultSet);}return blogList;}//3.根据博客id查询指定的博客public Blog getBlog(int blogId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);resultSet = statement.executeQuery();//此处拿着blogId进行查询,blogId作为主键,是唯一的//所以不用循环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) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,resultSet);}return null;}//4.根据博客id删除博客public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "select * from blog where blogId";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);statement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,null);}}
}

3.4 在java目录下创建UserDao类 

public class UserDao {//1.根据userId来查到对应的用户信息(获取用户信息)public User getUserById(int userId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from user where userId=?";statement = connection.prepareStatement(sql);statement.setInt(1,userId);resultSet = statement.executeQuery();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) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,resultSet);}return null;}//2.根据username来查到对应的用户信息(登录)public User getUserByName(String username) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from user where username = ?";statement = connection.prepareStatement(sql);statement.setString(1,username);resultSet = statement.executeQuery();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) {throw new RuntimeException(e);} finally {DBUtil.close(connection,statement,resultSet);}return null;}
}

4 获取博客列表页

在博客列表页加载的时候,通过ajax给服务器发起请求,从服务器(数据库)拿到博客列表页数据并显示到页面上

4.1 约定前后端交互接口

请求

GET / blog

响应

HTTP/1.1  200 OK

Content-Type:application/json

[

     { 

        blogId:1,

        title:"这是标题",

        content:"这是正文",

        postTime:"2024-4-10  9:00:00",

        userId:1

     }

]

4.2 让前端给服务器发起请求

引入第三方库

<script>//对这个函数进行封装function getBlogs() {$.ajax({type: 'get',url: 'blog',success: function (body) {//根据服务器返回的响应,构造页面的片段}});}//调用函数getBlogs();
</script>

4.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 blogDao = new BlogDao();//得到的是一个List数组,jackson就会把结果转成数组,数组里的每个元素又是一个Blog对象//转成数组的每个元素也就是json构成的blog对象List<Blog> blogs = blogDao.getBlogs();//把博客列表按照json格式返回给浏览器String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}

4.4 让前端处理上述响应数据

将响应的数据构造成HTML片段,显示到页面上

<script>//对这个函数进行封装function getBlogs() {$.ajax({type: 'get',url: 'blog',success: function (body) {//根据服务器返回的响应,构造页面的片段let containerDiv = document.querySelector('.container-right');for (let i = 0; i < body.length; i++) {//blog就是一个形如{blogId:1,title,"这是标题" ...}let blog = body[i];//构造整个博客let blogDiv = document.createElement('div');blogDiv.className = 'blog';//构建标题let titleDiv = document.createElement('div');titleDiv.className = 'title';titleDiv.innerHTML = blog.title;//构建发布时间let dateDiv = document.createElement('div');dateDiv.className = 'date';dateDiv.innerHTML = blog.postTime;//构建博客的摘要let descDiv = document.createElement('div');descDiv.className = 'desc';descDiv.innerHTML = blog.content;//构造全文查看按钮let a = document.createElement("a");a.innerHTML = '查看全文 &gt;&gt;'//点击不同的博客,跳转到不同的博客详情页a.href = 'blog_detail.html?blogId=' + blog.blogId;//把上面的操作进行组合blogDiv.appendChild(titleDiv);blogDiv.appendChild(dateDiv);blogDiv.appendChild(descDiv);blogDiv.appendChild(a);//把上面blogDiv最终组合到页面上containerDiv.appendChild(blogDiv);}}});}//调用函数getBlogs();
</script>

此时我们通过访问页面观察

页面展示的时间是一个时间戳,我们期望展示的时间是一个格式化的时间。

这里修改一下返回值的类型,让最终得到的时间是一个格式化时间 

此时我们的获取博客列表页就完成了 

5 获取博客详情页 

我们期望点击博客列表页不同的查看全文,会跳转过去并且URL后面会带有不同的query string

在博客详情页中,通过ajax给服务器发送请求,根据blogId来查询数据库中博客的具体内容在返回

 5.1 约定前后端交互接口

请求

GET / blog?blogId=1

响应

HTTP / 1.1 200 OK

Content-Type:application/json

{

        blogId:1,

        title:"这是第一篇博客",

        content:"这是博客正文",

        postTime:"2024-4-10  9:00:00",

        userId:1

}

5.2 让前端给服务器发起请求 

<script>function getBlog() {$.ajax({type:'get',//通过这个location.search方式拿到页面url中的query stringurl:'blog' + location.search,success:function(blog) {//这里的blog就是返回一篇博客的内容}});}getBlog();
</script>

5.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 blogDao = new BlogDao();String respJson = "";String blogId = req.getParameter("blogId");if (blogId == null) {//请求中没有query string,请求来自博客列表页List<Blog> blogs = blogDao.getBlogs();//把博客列表按照json格式返回给浏览器respJson = objectMapper.writeValueAsString(blogs);}else {//请求中有query string,请求来自博客详情页Blog blog = blogDao.getBlog(Integer.parseInt(blogId));respJson = objectMapper.writeValueAsString(blog);}resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}

5.4 让前端处理上述响应数据

<script>function getBlog() {$.ajax({type: 'get',//通过这个location.search方式拿到页面url中的query stringurl: 'blog' + location.search,success: function (blog) {//这里的blog就是返回一篇博客的内容let h3 = document.querySelector('.container-right h3');h3.innerHTML = blog.title;let dataDiv = document.querySelector('.container-right .date');dataDiv.innerHTML = blog.postTime;let contentDiv = document.querySelector('.container-right .content');contentDiv.innerHTML = blog.content;}});}getBlog();
</script>

此时我们的博客详情页就完成了,但是还有一个小问题,上述是md原始数据,这是因为content部分的内容是使用markdown写的,直接通过blog.content得到的正文内容是一个简单的文本格式,因此需要引入第三方库(editor.md)来完成渲染。

<!-- 引入 editor.md 的依赖 -->
<link rel="stylesheet" href="editor.md/css/editormd.min.css" />
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"></script>
<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>function getBlog() {$.ajax({type: 'get',//通过这个location.search方式拿到页面url中的query stringurl: 'blog' + location.search,success: function (blog) {//这里的blog就是返回一篇博客的内容let h3 = document.querySelector('.container-right h3');h3.innerHTML = blog.title;let dataDiv = document.querySelector('.container-right .date');dataDiv.innerHTML = blog.postTime;//这种设置方式,页面显示的是md原始数据// let contentDiv = document.querySelector('.container-right.content');// contentDiv.innerHTML = blog.content;//在这个方法的第一个参数,必须是一个html标签的id属性editormd.markdownToHTML('content', { markdown: blog.content });}});}getBlog();</script>

此时我们的博客详情页就彻底完成了 

6 实现登录

6.1 分析

1 在登录页面,在输入框中输入用户名和密码,点击登录,就会给服务器发起HTTP请求(这里我们使用form)

2 服务器在处理登录请求的时候,读取用户名和密码,在数据库中查询,如果匹配就登录成功,创建会话并跳转到博客列表页。

3 由于登录成功后直接进行重定向跳转,这种重定向跳转不需要前端写额外的代码。

 6.2 约定前后端交互接口

请求

POST/login

Content-Type:application/x-www-form-urlencoded

username=zhangsan&password=123

响应

HTTP/1.1 302

Location:blog_list.html

6.3 前端发起请求 

<form action="login" method="post"><div class="row"><span>用户名</span><input type="text" id="username" 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>

6.4 服务器处理响应

@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.读取参数中的用户名和密码req.setCharacterEncoding("utf8");String username = req.getParameter("username");String password = req.getParameter("password");//验证参数if (username == null || username.length() == 0 || password == null || password.length() == 0) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("输入的用户名或密码为空");return;}//2.查询数据库,看用户名密码是否正确UserDao userDao = new UserDao();User user = userDao.getUserByName(username);if (user == null) {//用户名不存在resp.setContentType("text/html;charset=utf8");resp.getWriter().write("输入的用户名或密码不正确");return;}if (!password.equals(user.getPassword())) {//密码不正确resp.setContentType("text/html;charset=utf8");resp.getWriter().write("输入的用户名或密码不正确");return;}//3.创建会话HttpSession session = req.getSession(true);session.setAttribute("user",user);//4.跳转到主页resp.sendRedirect("blog_list.html");}
}

此时实现登录就完成了 

7 实现强制登录

在博客列表页、详情页、编辑页,判断当前用户是否已经登陆,如果未登录,则强制跳转到登录页

7.1 约定前后端交互接口

请求

GET / login

响应

登陆成功:HTTP/1.1 200

登录失败:HTTP/1.1 404

7.2 让前端给服务器发起请求

//定义新的函数,获取登录状态
function getLoginStatus() {$.ajax({type: 'get',url: 'login',success: function (body) {//已经登录的状态console.log("已经登录了!");},error: function () {//error这里对应的回调函数就是在状态码不为2XX的时候出发//服务器返回403的时候,就会触发error部分的逻辑//这里强制页面跳转到博客登录页//这里不适用302这样的重定向跳转,是因为302这种响应无法被ajax直接处理location.assign('login.html');}});
}
getLoginStatus();

上面没有定义<script></script>标签是因为是在一个script标签里写的。

7.3 服务器处理上述请求,返回响应

@WebServlet("/login")
public class LoginServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//通过这个方法来反馈当前用户的登录状态//直接看会话是否存在//此处不仅要求会话存在,还要求会话中要保存user对象HttpSession session = req.getSession(false);if (session == null) {//会话不存在resp.setStatus(403);return;}User user = (User) session.getAttribute("user");if (user == null) {//user不存在resp.setStatus(403);return;}//说明两个都存在,直接返回200//此处默认就是200,不写也行resp.setStatus(200);}
}

 

当我们访问博客列表页就会跳转到博客登录页,接下来我们抓包看一下。 

第一请求就是我们访问博客列表数据

第二请求就是获取登录状态,此时返回的响应就是403

我们的目标是让博客列表页、详情页、编辑页都能够有强制登录

因此我们把获取登陆状态的代码,单独提出来,在博客列表页、详情页、编辑页中通过html中的script标签来引用这样的文件内容

现在我们的实现强制登录就完成了

8 实现显示用户信息

在博客列表页显示的是当前登录的用户信息,在博客详情页显示的是当前文章作者的信息,在页面加载的时候,给服务器发起ajax请求,服务器返回对应的用户数据,根据发起请求不同的页面,服务器返回不同的信息。

8.1 约定前后端交互接口

博客列表页,获取当前登录的用户信息

请求

GET / userInfo

响应

HTTP/1.1 200 OK

Content-Type:application/json

{

        userId:1,

        username:'zhangsan'

}

博客详情页,获取当前文章作者信息

请求

GET / authroInfo?blogId=1

响应

HTTP/1.1 200 OK

Content-Type:application/json

{

        userId:1,

        username:'zhangsan'

}

 8.2 让前端给服务器发起请求

博客列表页

//获取当前登录的用户信息
function getUserInfo() {$.ajax({type:'get',url:'userInfo',success:function(user) {}});
}
getUserInfo();

博客详情页

//获取作者信息
function getAuthorInfo() {$.ajax({type:'get',url:'authorInfo' + location.search,success:function(user) {}});
}

8.3 服务器处理上述请求,返回响应

博客列表页

@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//从会话中拿到用户的信息然后返回HttpSession session = req.getSession(false);if (session == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前用户未登录");return;}User user = (User) session.getAttribute("user");if (user == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前用户未登录");return;}//此时把user对象转成json字符串并返回给浏览器resp.setContentType("application/json;charset=utf8");//这里需要把密码去掉在返回user.setPassword("");String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);}
}

博客详情页 

@WebServlet("/authorInfo")
public class AuthorInfoServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.先拿到请求中的blogIdString blogId = req.getParameter("blogId");if (blogId == null) {resp.setContentType("text/html:charset=utf8");resp.getWriter().write("请求中缺少blogId");return;}//2.在blog表中查询对应的Blog对象BlogDao blogDao = new BlogDao();Blog blog = blogDao.getBlog(Integer.parseInt(blogId));if (blog == null) {resp.setContentType("text/html:charset=utf8");resp.getWriter().write("blogId没有找到");return;}//3.根据blog对象中的userId,从user表中查到对应的作者UserDao userDao = new UserDao();User user = userDao.getUserById(blog.getUserId());if (user == null) {resp.setContentType("text/html:charset=utf8");resp.getWriter().write("userId没有找到");return;}//4.把这个user对象返回给浏览器user.setPassword("");String respJson = objectMapper.writeValueAsString(user);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}

8.4 让前端处理上述响应数据

博客列表页

//获取当前登录的用户信息
function getUserInfo() {$.ajax({type: 'get',url: 'userInfo',success: function (user) {//把拿到的响应数据取出其中的uesrname设置到页面的h3标签中let h3 = document.querySelector('.card h3');h3.innerHTML = user.username;}});
}
getUserInfo();

通过抓包看一下详情

此时在博客列表页就能获取到用户的信息了

博客详情页

//获取作者信息
function getAuthorInfo() {$.ajax({type: 'get',url: 'authorInfo' + location.search,success: function (user) {let h3 = document.querySelector('.card h3');h3.innerHTML = user.username;}});
}
getAuthorInfo();

我们使用lisi这个用户登录,当我们点击查看全文就可以获取到当前文章的作者信息了,也就是zhangsan

此时我们的博客详情页就能获取到当前文章的作何信息了

此时我们的实现显示用户信息就完成了

9 实现退出功能

在博客列表页、博客详情页、博客编辑页的导航栏中都有一个"注销"按钮,让用户点击"注销" 的时候,就能触发一个HTTP请求

9.1 约定前后端交互接口

请求

GET / logout

响应  直接重定向到博客登录页

HTTP/1.1 302

Location:login.html

9.2  让前端给服务器发起请求

直接给a标签设置href属性即可

9.3 服务器处理上述请求,返回响应

@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//先拿到会话对象HttpSession session = req.getSession(false);if (session == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录");return;}//把会话中的user属性删除session.removeAttribute("user");//跳转到博客登录页resp.sendRedirect("login.html");}
}

我们在博客编辑页点击注销按钮,抓包看一下详情,我们可以看到页面会直接跳转到博客登录页

 此时我们的实现退出功能就完成了

10 实现发布博客

当用户点击提交的时候,构造HTTP请求,把此时页面中的标题和正文都传输到服务器这边,然后服务器把这个数据存入数据库,由于这里需要提交数据,因此使用form来构造请求

10.1 约定前后端交互接口

请求

POST/blog

Content-Type:x-www-form-urlencoded

title=这是标题&content=这是正文

响应

HTTP/1.1 302

Location:blog_list.html

10.2 让前端给服务器发起请求

10.3 服务器处理上述请求,返回响应

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//1.读取请求中的参数req.setCharacterEncoding("utf8");String title = req.getParameter("title");String content = req.getParameter("content");if (title == null || title.length() == 0 || content == null || content.length() == 0) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前传过来的标题或者正文为空,无法新增博客");return;}//2.拿到会话HttpSession session = req.getSession(false);if (session == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前用户未登录");return;}//3.从会话中拿到用户的信息User user = (User) session.getAttribute("user");if (user == null) {resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前用户未登录");return;}//4.构造blog对象Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);//我们能访问当前的博客编辑页,说明我们处于登录状态//因此我们从会话中拿到当前登录用户的userId,设置进去即可blog.setUserId(user.getUserId());//5.插入到数据库中BlogDao blogDao = new BlogDao();blogDao.insert(blog);//6.返回302重定向,跳转到博客列表页resp.sendRedirect("blog_list.html");}
}

此时我们写下博客然后提交,此时我们新写的博客就能再页面展示了

此时我们的实现发布博客功能就完成了


总结:整个过程是基于前后端交互来实现博客系统,主要是前后端交互的过程,以及前后端是怎样交互的

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/616645.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【STK】手把手教你利用STK进行导弹和反导仿真04 - STK/MMT模块03 导弹飞行工具应用案例

点击MFT的桌面快捷方式,启动MFT (1)首先建立新项目,点击Flight菜单中的New Flight选项,见下图 (2)项目设置。项目可以命名为Test;系统选项按默认设置,见下图;发射点可以选择。 飞行设置的工具栏就在附加窗口的中间,首先点击左边的General,修改这个项目的名字。然…

Zynq学习笔记--Vivado中自定义IP核

目录 1. 概述 2. IP Packager 重要参数 2.1 参数说明 3. AXI4 Lite 写操作逻辑 3.1 写数据请求发起 3.2 写响应通道握手 3.3 从机锁存数据 3.4 地址和数据处理 3.5 地址空间详解 4. 写入和读取请求 4.1 寄存器写入逻辑 4.2 写操作握手 4.3 读请求发起 4.4 读操作…

MySQL-变量、流程控制与游标:变量、定义条件与处理程序、流程控制

变量、流程控制与游标 变量、流程控制与游标1. 变量1.1 系统变量1.1.1 系统变量分类1.1.2 查看系统变量 1.2 用户变量1.2.1 用户变量分类1.2.2 会话用户变量1.2.3 局部变量1.2.4 对比会话用户变量与局部变量 2. 定义条件与处理程序2.1 案例分析2.2 定义条件2.3 定义处理程序2.4…

政安晨:【Keras机器学习实践要点】(三十)—— 使用斯温变换器进行图像分类

目录 设置 配置超参数 准备数据 辅助函数 基于窗口的多头自注意力计算 模型训练与评估 准备 tf.data.Dataset 建立模型 在 CIFAR-100 上训练 政安晨的个人主页&#xff1a;政安晨 欢迎 &#x1f44d;点赞✍评论⭐收藏 收录专栏: TensorFlow与Keras机器学习实战 希望政…

深入微服务框架:构建高效、可扩展与弹性的现代应用架构

前言&#xff1a;当今快速迭代和多变的商业环境中&#xff0c;传统的单体应用程序面临着一系列挑战&#xff0c;包括难以管理复杂性、缺乏灵活性以及无法有效扩展等问题。随着业务需求的不断增长和技术栈的不断演进&#xff0c;企业亟需一种更加模块化、易于管理和扩展的应用程…

【Sql Server】锁表如何解锁,模拟会话事务方式锁定一个表然后进行解锁

大家好&#xff0c;我是全栈小5&#xff0c;欢迎来到《小5讲堂》。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解。 温馨提示&#xff1a;博主能力有限&#xff0c;理解水平有限&#xff0c;若有不对之处望指正&#xff01; 目录 前言创建表模拟…

LLM生成模型在生物单细胞single cell的应用:scGPT

参考&#xff1a; https://github.com/bowang-lab/scGPT https://www.youtube.com/watch?vXhwYlgEeQAs 主要是把单细胞测序出来的基因表达量的拼接起来构建成的序列&#xff0c;这里不是用的基因的ATCG&#xff0c;是直接用的基因名称 训练数据&#xff1a;scGPT全人模型是在3…

蓝桥杯备赛:考前注意事项

考前注意事项 1、DevCpp添加c11支持 点击 工具 - 编译选项 中添加&#xff1a; -stdc112、万能头文件 #include <bits/stdc.h>万能头文件的缺陷&#xff1a;y1 变量 在<cmath>中用过了y1变量。 #include <bits/stdc.h> using namespace std;// 错误示例 …

数据结构排序篇上

排序的概念及其运用 排序的概念 排序 &#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性 &#xff1a;假定在待排序的记录序列中&#xff0c;存在多个具有相同的关键字的记录&…

口型动画论文2:《基于语音驱动的表情动画设计与实现》

说明 本文是北京邮电大学的硕士毕业论文&#xff0c;作者是郭梦婷。由于是艺术硕士&#xff0c;所以本文没有罗列很多公式&#xff0c;而是从动画创作的角度来写如何根据语音设计动画人物的嘴型及表情。本文作者行文缜密、轻松&#xff0c;举得例子都是一些热播的动画和电影&a…

Redis:发布和订阅

文章目录 一、介绍二、发布订阅命令 一、介绍 Redis的发布和订阅功能是一种消息通信模式&#xff0c;发送者&#xff08;pub&#xff09;发送消息&#xff0c;订阅者&#xff08;sub&#xff09;接收消息。这种功能使得消息发送者和接收者不需要直接建立连接&#xff0c;而是通…

Zotero插件ZotCard中AI-NNDL文献笔记卡分享及卡片使用方法

一、卡片社区分享 github&#xff1a;ZotCard插件AI-NNDL论文卡片模板 Issue #67 018/zotcard (github.com) 二、卡片效果预览 ZotCard插件AI-NNDL论文卡片模板是关于人工智能神经网络与深度学习论文的笔记卡片&#xff0c;效果预览如下图&#xff1a; 三、卡片代码 经过了…