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.mzph.cn/news/814411.shtml

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

相关文章

3D场景编辑方法——CustomNeRF

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 摘要Abstract文献阅读&#xff1a;3D场景编辑方法——CustomNeRF1、研究背景2、提出方法3、CustomNeRF3.1、整体框架步骤3.2、对特定问题的解决 4、实验结果5、总结…

组合模式:构建树形对象结构的设计艺术

在软件开发中&#xff0c;组合模式是一种结构型设计模式&#xff0c;用于表示对象的部分-整体层次结构。通过使单个对象和组合对象具有相同的接口&#xff0c;这种模式允许客户端以统一的方式处理单个对象和组合对象。本文将详细介绍组合模式的定义、实现、应用场景以及优缺点。…

2024HW--->入侵排查

在蓝队的面试中&#xff0c;我们有可能会被问到对可能被入侵的机器&#xff0c;怎么样去排查&#xff0c;下面就来总结一下 1.Windows入侵排查 1.检查系统账号的安全 检测系统账号&#xff0c;其实最重要的就是一个点 "查看服务器是否存在可疑账号、新增账号。" 最…

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

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

院子里种点什么树风水好呢?

植物本身是一个丰富的生活领域&#xff0c;有着强烈的视觉暗示。其实&#xff0c;在家中养植物&#xff0c;是有许多好处的&#xff0c;它不仅能够装点庭院的环境让家更美丽&#xff0c;还能调节室内的空气质量&#xff0c;对家人的运势也有着非常大的帮助。 不过&#xff0c;并…

你的高佣副业不二之选,开始流量卡推广,一文看懂号卡推广

在这个信息化的时代&#xff0c;网络已成为人们生活中不可或缺的一部分。然而&#xff0c;在享受着便捷与高效的同时&#xff0c;我们也必须面对一个现实问题&#xff0c;也就是高昂的流量费用。为了解决这一困扰广大用户的痛点&#xff0c;我们今天带来了一个极具吸引力的机会…

005Node.js模块URL的使用

引入 URL 模块 要使用 URL 模块&#xff0c;首先需要在代码中引入它。可以使用以下代码将 URL 模块导入到你的脚本中&#xff1a; const url require(url);实例代码 const urlrequire(url); var apihttp://www.baidu.com?nameshixiaobin&age20; console.log(url.parse(…

云服务器环境web环境搭建之JDK、redis、mysql

一、Linux安装jdk&#xff0c;手动配置环境 链接: https://pan.baidu.com/s/1LRgRC5ih7B9fkc588uEQ1whttps://pan.baidu.com/s/1LRgRC5ih7B9fkc588uEQ1w 提取码: 0413 tar -xvf 压缩包名 修改配置文件/etc/profile 二、安装redis环境 方案一&#xff1a; Linux下安装配置r…

【2024年5月备考新增】《软考真题分章练习(含答案解析) - 18 管理科学-运筹学基础 (2)》

21、某种商品价格 P 变动与某指标 A 的变化具有很强的相关性,指标 A 的增长会导致 P 的降低,反之亦然。指标 A 和价格 P 的相关性系数是()。 A.0.18 B.0 C.0.98 D.-0.83 【答案】D 【解析】A 的增长会导致 B 的降低,反比关系,系数必然是一个负数。正比函数 y=kx,当 k>…

网站建设也会涉及商标侵权,需要注意些!

以前普推知产老杨碰到建站涉及知识产权侵权的&#xff0c;但是大多数是其它方面的&#xff0c;前几天看到某同行说由于给客户建设网站&#xff0c;由于网站名称涉及商标被起诉要索赔几十万。 当时同行给做网站时还看了下营业执照&#xff0c;上面的主体名称与网站名称也是一致…

乡村智慧化升级:数字乡村打造农村生活新品质

目录 一、乡村智慧化升级的内涵与意义 二、乡村智慧化升级的具体实践 1、加强农村信息基础设施建设 2、推广智慧农业应用 3、提升乡村治理智慧化水平 4、丰富智慧乡村生活内容 三、数字乡村打造农村生活新品质的成果展现 1、农业生产效率与质量双提升 2、农民收入与消…

主干网络篇 | YOLOv8更换主干网络之VanillaNet | 华为方舟实验室提出全新轻量级骨干架构

前言:Hello大家好,我是小哥谈。华为方舟实验室所提出的VanillaNet架构克服了固有复杂性的挑战,使其成为资源受限环境的理想选择。其易于理解和高度简化的架构为高效部署开辟了新的可能性。广泛的实验表明,VanillaNet提供的性能与著名的深度神经网络和vision transformers相…

【数学】主成分分析(PCA)的详细深度推导过程

本文基于Deep Learning (2017, MIT)&#xff0c;推导过程补全了所涉及的知识及书中推导过程中跳跃和省略的部分。 blog 1 概述 现代数据集&#xff0c;如网络索引、高分辨率图像、气象学、实验测量等&#xff0c;通常包含高维特征&#xff0c;高纬度的数据可能不清晰、冗余&am…

开源项目one-api的k8s容器化部署(上)-- 制作镜像及部署准备

一、背景 最近需要对开源项目one-api进行k8s容器化部署&#xff0c;主要分以下几个步骤&#xff1a; 制作docker镜像申请mysql和redis数据库docker-compose部署方式k8s部署方式 整个的篇幅比较长&#xff0c;将会分成上下两篇来阐述。 二、制作docker镜像 开源项目one-api…

Linux的学习之路:9、冯诺依曼与进程(1)

摘要 本章主要是说一下冯诺依曼体系结构和进程的一部分东西。 目录 摘要 一、冯诺依曼体系结构 二、操作系统的概念 三、设计OS的目的 四、管理 五、进程的基本概念 六、PCB 七、在Linux环境下查看进程 八、使用代码创建进程 九、思维导图 一、冯诺依曼体系结构 如…

5G Frequency Bands 频率分布

连接&#xff1a;https://www.5g-networks.net/5g-technology/5g-frequency-bands/

最新视频理解大模型之MiniGPT4-video

前言 随着大模型的爆火&#xff0c;多模态大模型也随之卷了起来&#xff0c;基本每隔一小段时间就会冒出一个新模型。 今天给大家带来一个最新发现的关于视频理解的多模态大模型。 它的名字是MiniGPT4-video&#xff0c;可以看的出来其是MiniGPT4的一个分支&#xff1b;Mini…

STM32利用软件I2C通讯读MPU6050的ID号

今天的读ID号是建立在上篇文章中有了底层的I2C通讯的6个基本时序来编写的。首先需要完成的就是MPU6050的初始化函数 然后就是编写 指定地址写函数&#xff1a; 一&#xff1a;开始 二&#xff1a;发送 从机地址读写位&#xff08;1&#xff1a;读 0&#xff1…

ESP-IDF移植lvgl 驱动 ST7789

文章目录 1 前言2 准备3 移植LVGL3.1 工程准备3.2 修改 CMakeLists.txt文件编译 LVGL3.3 编译LVGL 4 编译 ST7789 LCD驱动5 发现问题 1 前言 本教程开始学习 LVGL的&#xff0c;开始之前要把环境配置好&#xff0c;首先就需要移植 lvgl&#xff0c;使用的是 esp32 环境&#xf…

kafka学习记录

文章目录 windows单机版kafka搭建步骤主题的增删改查操作消息的生产与消费 Windows集群版kafka搭建步骤 prettyZoo 尚硅谷Kafka教程&#xff0c;2024新版kafka视频&#xff0c;零基础入门到实战 【尚硅谷】Kafka3.x教程&#xff08;从入门到调优&#xff0c;深入全面&#xff0…