一:博客前端代码
(1)获取前端代码
(2)页面分类
①博客详情页:点击列表页中的某个博客,就能跳转到详情页
②博客列表页:表示一个博客网站中都有哪些博客
③博客登录页:即用户登录页面
④博客编辑页:可以在此编辑并发布博客
(博客编辑页这里我们引入了第三方库editor.md;可以让我们编写markdown格式内容)
二:后端的准备工作和思路
(1)准备工作
①创建项目,取名为blog_system
②打开中央仓库,在pom.xml引入依赖
(依赖分别是Servlet、mysql、jackson)
<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.49</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.2</version> </dependency> </dependencies>
③创建必要的目录,给web.xml写入必要的代码
//web.xml里的代码(复制后把这段注释删掉!) <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app> <display-name>Archetype Created Web Application</display-name> </web-app>
④把前端的文件夹blog_system里的所有文件复制到webapp目录下
⑤创建SmartTomcat,让网页顺利运行
(2)大体思路
💛大体思路主要分为两个部分
①前端和后端(服务器)的交互
②后端(服务器)和数据库的交互
💜先②再①,即先进行后端(服务器)与数据库的交互,再来搞前后端交互
三:后端(服务器)和数据库的交互
💗思路:
1.创建数据库和数据表
2.使用JDBC在DBUtil中封装数据库的连接和断开操作
3.创建实体类,后续数据库操作围绕实体类展开
4.创建不同实体的Dao类,通过不同Dao类针对不同表进行增删改查
(1)创建数据库和数据表
🌟①先创建数据库和数据表
💙此时需要先在main目录下创建一个db.sql文件,在此文件下创建数据库和数据表
🌟②在db.sql文件下编写代码
-- 在此文件下编写SQL完成建库建表操作 -- 创建数据库 create database if not exists hlizoo_blog_system charset utf8; use hlizoo_blog_system; -- 创建两个表,一个博客表,一个表示用户表 -- 博客表用来管理博客,用户表用来管理用户 -- 我们一般的操作会先删表再重新创建,防止之前的表的数据对我们产生不好的影响 drop table if exists user; drop table if exists blog; create table blog( -- 博客的编号 blogId int primary key auto_increment, -- 博客的标题 title varchar(256), -- 博客的内容 content varchar(4096), -- 博客的作者 userId int, -- 博客的发布时间 postTime datetime ); create table user ( -- 用户编号 userId int primary key auto_increment, -- 用户名, 约定用户名不能重复. username varchar(64) unique, -- 密码 password varchar(64) -- user 里还可以增加很多别的属性. github 链接, 头像链接..... ); -- 构造一些初始数据,方便后续的测试 insert into user values(1, 'zhangsan', '123'), (2, 'lisi', '123'); insert into blog values(1,'这是我的第一篇博客','从今天起我要好好敲代码',1,'2023-9-25 21:17:30'); insert into blog values(2,'这是我的第二篇博客','从昨天起我要好好敲代码',1,'2023-9-26 23:07:11'); insert into blog values(3,'这是我的第三篇博客','从前天起我要好好敲代码',1,'2023-9-27 18:20:00');
🌟③把在db.sql文件下编写的SQL代码复制到Mysql数据库去执行
(然后就可以在数据库里去查询数据了)
(2)了解dao包
1.dao包的概念
💗Data Access Object,即数据访问对象
2.dao包的作用
💗即通过在dao包下创建和写一些类,通过这些类里的方法封装数据库的操作,此时数据库就可以通过这些类的对象来操作,总的来说,数据库的操作一般都放在dao包下
3.dao包的创建
💗一般创建于src/main/java中,即创建在Java目录下
(3)DBUtil中封装数据库的连接和断开操作
①在dao包下创建一个Java文件,取名为DBUtil
(通过这个类,把数据库建立连接的逻辑进行封装)
②在DBUtil文件下,我们会使用单例模式组织DataSource
(通过DBUtil,实现数据库的连接和断开操作)
package dao; import com.mysql.jdbc.jdbc2.optional.MysqlDataSource; import javax.sql.DataSource; import java.sql.*; //通过这个类,把数据库建立连接的逻辑进行封装 public class DBUtil { private static volatile DataSource dataSource = null; //获取数据库的连接 public static DataSource getDataSource(){ if (dataSource == null){ synchronized (DBUtil.class){ if(dataSource == null){ dataSource = new MysqlDataSource(); ((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/hlizoo_blog_system?characterEncoding=utf8&AllowPublicKeyRetrieval=True"); ((MysqlDataSource)dataSource).setUser("root"); ((MysqlDataSource)dataSource).setPassword("hlizoo777"); } } } return dataSource; } //提供一个方法,和数据库进行连接 public static Connection getConnection() { try { return getDataSource().getConnection(); } catch (SQLException e) { e.printStackTrace(); } return null; } //提供一个方法,和数据库断开连接 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(); } } } }
(4)创建实体类
1.实体
💗数据库的设计,就是先要根据需求找出有哪些实体,实体就是比如职工、学生、教师、课程等都是实体,然后梳理实体与实体之间的关系
2.实体类
💛实体类是需要和数据库中的表存在对应关系
💜通常,每个表都需要一个实体类
💜后续,就可以通过这个实体类的对象来表示表里的每一条记录
💜因此,这个实体类对象的属性,要求和表里的每一列都要对应
3.创建实体类
①由于我们只有两张表,我们只需要创建两个实体类
(在dao包下创建两个类,一个叫Blog,一个叫User)
②创建Blog实体类
package dao; import java.sql.Timestamp; //通过Blog这个类的对象,表示一条blog表中的记录 //这个Blog类的属性,要和blog表里的每一列匹配 public class Blog { private int blogId; private String title; private String content; private int userId; //Sql里面有timestamp和datatime来表示日期的类型,但在sql中我们建议用datatime,因为timestamp只能表示到2038年 //而在Java里,timesatmp没有这个限制 private Timestamp postTime; 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 int getUserId() { return userId; } public void setUserId(int userId) { this.userId = userId; } public Timestamp getPostTime() { return postTime; } public void setPostTime(Timestamp postTime) { this.postTime = postTime; } @Override public String toString() { return "Blog{" + "blogId=" + blogId + ", title='" + title + '\'' + ", content='" + content + '\'' + ", userId=" + userId + ", postTime=" + postTime + '}'; } }
③创建User实体类
package dao; //通过User这个类的对象,表示一条user表中的记录 //这个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; } @Override public String toString() { return "User{" + "userId=" + userId + ", username='" + username + '\'' + ", password='" + password + '\'' + '}'; } }
(5)创建Dao类
1.创建BlogDao类
🌟通过BlogDao类,封装针对blog表进行增删改查
💗代码:package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; //通过BlogDao类,封装针对blog表进行增删改查 public class BlogDao { //1.新增一个博客 //注:调用insert时,需要先构造一个Blog对象,再作为参数传递给insert,最后由insert内部完成数据库插入操作 public void insert(Blog blog) { Connection connection = null; PreparedStatement statement = null; try{ //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句进行插入操作 //此处的博客发布时间,正好是执行SQL时刻,直接使用SQL里的库函数now,用来获取当前时间 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)执行SQL语句 statement.executeUpdate(); }catch (SQLException e){ e.printStackTrace(); } finally { //(4)关闭连接 DBUtil.close(connection,statement,null); } } //2.查询blog表里所有的博客 public List<Blog> getBlogs() { Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; List<Blog> blogs = new ArrayList<>(); try { //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句进行查询操作 //使用order by是为了让新发布的博客先展示 String sql = "select * from blog order by postTime desc"; statement = connection.prepareStatement(sql); //(3)执行SQL语句 resultSet = statement.executeQuery(); //(4)遍历结果集合 while (resultSet.next()){ Blog blog = new Blog(); blog.setBlogId(resultSet.getInt("blogId")); blog.setTitle(resultSet.getString("title")); blog.setContent(resultSet.getString("content")); blog.setUserId(resultSet.getInt("userId")); blog.setPostTime(resultSet.getTimestamp("postTime")); blogs.add(blog); } }catch(SQLException e){ e.printStackTrace(); }finally { //(5)关闭连接 DBUtil.close (connection,statement,resultSet); } return blogs; } //3.指定一个blogId来查询某一个博客 public Blog getblog(int blogId){ Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句进行查询操作 String sql = "select * from blog where blogId = ?"; statement = connection.prepareStatement(sql); statement.setInt(1,blogId); //(3)执行SQL语句 resultSet = statement.executeQuery(); //(4)遍历结果集合 while (resultSet.next()){ Blog blog = new Blog(); blog.setBlogId(resultSet.getInt("blogId")); blog.setTitle(resultSet.getString("title")); blog.setContent(resultSet.getString("content")); blog.setUserId(resultSet.getInt("userId")); blog.setPostTime(resultSet.getTimestamp("postTime")); return blog; } }catch(SQLException e){ e.printStackTrace(); }finally { //(5)关闭连接 DBUtil.close(connection,statement,resultSet); } return null; } //4.指定一个blogId来删除某一个博客 public void delete(int blogId){ Connection connection = null; PreparedStatement statement = null; try{ //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句进行插入操作 //此处的博客发布时间,正好是执行SQL时刻,直接使用SQL里的库函数now,用来获取当前时间 String sql = "delete from blog where blogId = ?"; statement = connection.prepareStatement(sql); statement.setInt(1,blogId); //(3)执行SQL语句 statement.executeUpdate(); }catch (SQLException e){ e.printStackTrace(); } finally { //(4)关闭连接 DBUtil.close(connection,statement,null); } } }
2.创建UserDao类
🌟通过UserDao类,封装针对user表进行增删改查
💗代码:
package dao; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; //通过UserDao类,封装针对user表进行增删改查 public class UserDao { // 对于新增User,其实就是实现一个“注册”功能,这里我们暂时不实现 // 对于删除User,其实就是实现一个“注销”功能,这里我们暂时不实现 //1.根据userId来查询用户信息(方便我们后续根据博客查询出作者详情) public User getUserById(int userId){ Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句 String sql = "select * from user where userId = ?"; statement = connection.prepareStatement(sql); statement.setInt(1,userId); //(3)执行SQL语句 resultSet = 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; } //2.根据username来查询用户信息(主要是为了实现一个登录的效果) public User getUserByName(String username){ Connection connection = null; PreparedStatement statement = null; ResultSet resultSet = null; try { //(1)与数据库建立连接 connection = DBUtil.getConnection(); //(2)构造SQL语句 String sql = "select * from user where username = ?"; statement = connection.prepareStatement(sql); statement.setString(1,username); //(3)执行SQL语句 resultSet = 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; } }
四:前后端的交互
(1)整体思路
💗思路:以每个功能点为维度展开
🌟针对每个功能点分别进行以下操作
①设计前后端交互接口➜②设计后端代码➜③设计前端代码➜④调试
功能一:实现博客列表页;让博客列表页能够加载博客列表
功能二:实现博客详情页;当我们点击某一篇博客的“查看全文”时能看到整篇博客
功能三:实现博客登录页;输入用户的账号和密码;然后重定向到博客列表页
功能四:实现强制检查登录;如果用户在未登录情况下访问博客列表页/详情页/编辑页,都会自动跳转到登录页
功能五:实现显示个人信息;在博客列表页左侧显示当前登录用户的信息;在博客详情页左侧显示博客作者信息
功能六:实现退出登录功能
功能七:实现发布博客功能;在博客编辑页面写出博客后将其展示在博客列表页,保存于数据库中
(2)实现功能一
💗思路与步骤:
①先由前端发起一个HTTP请求,向后端索要博客列表数据
②后端收到请求之后查询数据库获取到数据库中的博客列表,并返回给前端
③前端拿到响应之后,会根据这里的内容,构造出html片段,最终显示出来
1.设计前后端交互接口
💚在进行上述这三个步骤之前,先要约定前后端交互接口,即约定请求和响应分别是啥
2.设计后端代码
💙后端代码要做的,就是当收到一个上述格式的请求之后,就构造并返回一个约定的响应数据给前端即可
①在main/java目录下创建一个包,取名为api
②在api包下创建一个类,取名为BlogServlet
(注意,此处写的所有东西都得遵守上述的约定,即请求和响应都应该按约定好的写)
package api; import com.fasterxml.jackson.databind.ObjectMapper; import dao.Blog; import dao.BlogDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; //因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定 @WebServlet("/blog") public class BlogServlet extends HttpServlet { //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据 private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //此处的工作:查询数据库,获取到数据库中的数据之后,构造成要求的json格式并返回 BlogDao blogDao = new BlogDao(); List<Blog> blogs = blogDao.getBlogs(); //此处就会把博客列表blogs构造成json格式 String respJson = objectMapper.writeValueAsString(blogs); //写到响应并返回 resp.setContentType("application/json;charset=utf8"); resp.getWriter().write(respJson); } }
3.设计前端代码
💙前端代码要做的,就是构造请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上
①使用VsCode打开blog_list.html这个前端代码
②把右侧信息的大部分代码都删除掉,只留下如图所示的部分
③引入jQuery
要让前端和后端交互,就需要使用ajax
这里的ajax我们不使用原生的,而是使用jQuery提供的
具体可点击➜应用层-详解HTTP协议(目录九)
④构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上
💜(只修改右侧信息)
<!-- 右侧信息 --> <div class="container-right"> <!-- <div class="blog"> <div class="title">我的第一篇博客博客博客博客</div> <div class="date">2023-05-11 20:00:00</div> <div class="desc"> 从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem. </div> <a href="blog_detail.html?blogId=1">查看全文 >> </a> </div> --> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script> //编写JS代码 //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上 function getBlogs(){ $.ajax({ type:'get', url:'blog', success:function(body){ //在这里根据响应的内容,构造出html片段,展示到页面上 //由于在服务器响应中已经设置了Content-Type为application/json //此时jQuery就能够自动把此处响应的内容解析成js对象数组 let container = document.querySelector('.container-right'); for(let blog of body){ //这个for循环就相当于Java里的for-each循环 //根据当前这个blog,构造出一个html片段 //构造的格式按上面注释部分来 //构造blog的div标签 let 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.href = 'blog_detail.html?blogId=' + blog.blogId; a.innerHTML = '查看全文 >>'; blogDiv.appendChild(a); //最后一步:将拼好的blogDiv添加到container container.appendChild(blogDiv); } } }) } getBlogs(); </script>
⑤效果展示
💜(1)得益于前面order by排序,使得新发布的博客先展示在前面
💜(2)这些数据全都是数据库里面存储的
⑥引出问题-时间格式
🌟问题:时间格式不对,页面展示的是时间戳,这并不方便我们去观察
🔯1.先明确问题如何出现
答:后端服务器返回的就是一个时间戳,且前端没有做任何处理,就导致最后显示了时间戳
🔯2.如何解决
答:使用Java标准库提供的SimpleDateFormat类,使用SimpleDateFormat可以将日期对象转换为指定格式的字符串,或将字符串解析为对应的日期对象
🔯3.如何使用SimpleDateFormat
import java.text.SimpleDateFormat; import java.util.Date; //SimpleDateFormat使用步骤 public class date { public static void main(String[] args) { //(1)先创建SimpleDateFormat对象,指定日期格式 SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); //(2)format()方法将当前日期格式化为指定格式的字符串 Date date = new Date(); String formattedDate = sdf.format(date); System.out.println("格式化后的日期:" + formattedDate); } }
🔯4.在dao包下的Blog文件中修改getPostTime()方法
public String getPostTime() { //此处需要把时间戳格式化 //return postTime; SimpleDateFormat format_time = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss"); return format_time.format(postTime); }
🔯5.效果马上就变成了如图所示
(3)实现功能二
💗思路与步骤:
①先由前端发起一个HTTP请求,向后端索要指定的博客内容数据
②后端收到请求之后查询数据库获取到数据库中指定的博客内容数据,并返回给前端
③前端拿到响应之后,会根据这里的内容,构造出html片段,最终显示出来
1.设计前后端交互接口
💚在进行上述这三个步骤之前,先要约定前后端交互接口,即约定请求和响应分别是啥
2.设计后端代码
💙后端代码要做的,就是当收到一个上述格式的请求之后,就构造并返回一个约定的响应数据给前端即可
🌟依旧在BlogServlet文件下修改代码
package api; import com.fasterxml.jackson.databind.ObjectMapper; import dao.Blog; import dao.BlogDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.List; //因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定 @WebServlet("/blog") public class BlogServlet extends HttpServlet { //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据 private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页 String blogId = req.getParameter("blogId"); BlogDao blogDao = new BlogDao(); if (blogId==null){ //如果blogId为空,说明query string为空,此时就是博客列表页 //此处的工作:查询数据库,获取到数据库中的所有博客数据之后,构造成要求的json格式并返回 List<Blog> blogs = blogDao.getBlogs(); //此处就会把博客列表blogs构造成json格式 String respJson = objectMapper.writeValueAsString(blogs); //写到响应并返回 resp.setContentType("application/json;charset=utf8"); resp.getWriter().write(respJson); }else { //如果blogId不为空,说明query string不为空,此时就是博客详情页 //根据不同的blogId查看到不同的blog对象和数据 //注意:这里是单个博客,而不是所有博客 Blog blog = blogDao.getblog(Integer.parseInt(blogId)); if (blog==null){ //如果blog为空,也就是blogId不存在没有这篇博客 //就返回一个blogId为0的blog对象,因为blogId不可能为0,此时再由前端来进行判定 blog=new Blog(); } //将指定的博客数据构造成json格式 String respJson = objectMapper.writeValueAsString(blog); //写到响应并返回 resp.setContentType("application/json;charset=utf8"); resp.getWriter().write(respJson); } } }
3.设计前端代码
💙前端代码要做的,就是构造请求向服务器获取到指定blogId的具体博客数据,然后接收服务器返回的响应并展示到页面上
①使用VsCode打开blog_detail.html这个前端代码
②把右侧信息的大部分代码都删除掉,只留下如图所示的部分
(把content的标签改为<div id>)
🌟到时候构造页面内容,我们就根据这个格式去构造
③引入jQuery
④使用ajax
💜(只修改右侧信息)
<!-- 右侧信息 --> <div class="container-right"> <h3></h3> <div class="date"></div> <div id="content"> </div> </div> </div> <!-- 引入 ajax 的依赖 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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', url:'blog' + location.search, success:function(body){ //根据服务器返回的响应内容,构造页面 //第一步:构造标题 let h3 = document.querySelector('.container-right h3'); h3.innerHTML = body.title; //第二步:构造时间 let dateDiv = document.querySelector('.container-right .date'); dateDiv.innerHTML = body.postTime; //第三步:构造内容 //格式:editormd.markdownToHTML('content',{markdown:markdown}); editormd.markdownToHTML('content',{markdown:body.content}); } }); } getBlog(); </script>
⑤效果展示
如图所示:第三篇博客的详情页
(4)实现功能三
💗思路与步骤:
①在页面中输入用户的账号和密码
②点击登录之后触发登录逻辑,比如验证用户的账号和密码是否正确,创建Session等等
③如果登录成功,则重定向到博客列表页
1.设计前后端交互接口
2.设计后端代码
💙需要写一个新的Servlet,处理login这个请求的路径
(每个Servlet都需要关联一个路径,像之前的博客列表页和博客详情页,因为它们的路径都是blog,所以共用一个Servlet即可;但是login是新的路径,我们需要创建一个新的Servlet)
💛在api包下创建新的文件,取名为LoginServlet
package api; import dao.User; import dao.UserDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //第一步:获取请求中的用户名和密码 //(给请求req设置字符集utf8,保证传过来的用户名和密码即使是中文,也能够正确处理) req.setCharacterEncoding("utf8"); String username = req.getParameter("username"); String password = req.getParameter("password"); if (username==null || password==null || "".equals(username) || "".equals(password)){ //进入if循环,说明提交的用户名和密码有错误;此时我们返回错误信息 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("当前传过来的username或者password为空"); return; } //第二步:代码来到这,说明用户名和密码已经输入,此时需要去数据库验证用户名和密码是否正确 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; } //第三步:代码来到这,说明用户名和密码正确,我们则创建会话 HttpSession session = req.getSession(true); //把当前登录的用户信息保存到session中,方便后续进行获取 session.setAttribute("user",user); //第四步:跳转到博客列表页 resp.sendRedirect("blog_list.html"); } }
3.设计前端代码
💙调整之前的登录页,让这个页面能够构造出form表单的请求
①使用VsCode打开login.html这个前端代码
②form表单在代码中已经写的差不多了,我们只需要稍作修改即可
③具体代码
<!-- 登录页的版心 --> <div class="login-container"> <!-- 登录对话框 --> <div class="login-dialog"> <h3>登录</h3> <!-- 使用 form 包裹一下下列内容, 便于后续给服务器提交数据 --> <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> </div> </div>
④查看数据库有哪些用户和密码
⑤效果展示-当输入的用户名或者密码有误时
⑥效果展示-当输入正确时
(5)实现功能四
1.设计前后端交互接口
2.设计后端代码
💙在LoginServlet文件下添加一个doGet方法,用来检测是否登录
(前面的代码都一样,主要看下面的doGet方法)
package api; import dao.User; import dao.UserDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/login") public class LoginServlet extends HttpServlet { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //第一步:获取请求中的用户名和密码 //(给请求req设置字符集utf8,保证传过来的用户名和密码即使是中文,也能够正确处理) req.setCharacterEncoding("utf8"); String username = req.getParameter("username"); String password = req.getParameter("password"); if (username==null || password==null || "".equals(username) || "".equals(password)){ //进入if循环,说明提交的用户名和密码有错误;此时我们返回错误信息 resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("当前传过来的username或者password为空"); return; } //第二步:代码来到这,说明用户名和密码已经输入,此时需要去数据库验证用户名和密码是否正确 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; } //第三步:代码来到这,说明用户名和密码正确,我们则创建会话 HttpSession session = req.getSession(true); //把当前登录的用户信息保存到session中,方便后续进行获取 session.setAttribute("user",user); //第四步:跳转到博客列表页 resp.sendRedirect("blog_list.html"); } //通过这个方法,来检测当前登录状态 @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //尝试获取对话session,会话不存在则认为未登录 HttpSession session = req.getSession(false); if (session==null){ //未登录状态 resp.setStatus(403); return; } //此时session虽然存在,但是还需要判断user对象是否存在(也是为了实现后面的“退出登录”功能) User user = (User) session.getAttribute("user"); if (user==null){ //未登录状态 resp.setStatus(403); return; } //来到这里证明用户已经登录 resp.setStatus(200); } }
3.设计前端代码
💙分别在博客列表页/博客详情页/博客编辑页添加一个get方法的ajax请求,用来判断是否登录
①因为每个页面都需要添加一个get方法的ajax请求,所以我们先在WEBAPP下创建一个文件夹,取名为js,再在js文件夹下面添加一个文件,取名为app.js
②在app.js文件下写代码
function checkLogin(){ $.ajax({ type:'get', url:'login', success:function(body){ //表示登录成功,此时啥都不需要做 }, error:function(body){ //表示未登录,返回403的情况会触发这个逻辑 //跳转到登录页面 location.assign("login.html"); } }) }
③在博客列表页/详情页/编辑页中先引入这个JS文件,然后调用该checkLogin()方法
(1)博客列表页blog_list.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>博客列表页</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/blog_list.css"> </head> <body> <!-- 导航栏. nav 是 导航 这个词的缩写 --> <div class="nav"> <!-- logo --> <img src="image/logo2.jpg" alt=""> <div class="title">我的博客系统</div> <!-- 只是一个空白, 用来把后面的链接挤过去 --> <!-- 这是一个简单粗暴的写法~~ --> <div class="spacer"></div> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <!-- 这里的地址回头再说 --> <a href="">注销</a> </div> <!-- 页面的主体部分 --> <div class="container"> <!-- 左侧信息 --> <div class="container-left"> <!-- 这个 div 表示整个用户信息的区域 --> <div class="card"> <!-- 用户的头像 --> <img src="image/kun.jpg" alt=""> <!-- 用户名 --> <h3>hlizoo</h3> <!-- github 地址 --> <a href="https://www.github.com">github 地址</a> <!-- 统计信息 --> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>1</span> </div> </div> </div> <!-- 右侧信息 --> <div class="container-right"> <!-- <div class="blog"> <div class="title">我的第一篇博客博客博客博客</div> <div class="date">2023-05-11 20:00:00</div> <div class="desc"> 从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem. </div> <a href="blog_detail.html?blogId=1">查看全文 >> </a> </div> --> </div> </div> </div> <!-- 引入 ajax 的依赖 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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> <!-- 引入 app.js,判断是否登录 --> <script src="js/app.js"></script> <script> //编写JS代码 //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上 function getBlogs(){ $.ajax({ type:'get', url:'blog', success:function(body){ //在这里根据响应的内容,构造出html片段,展示到页面上 //由于在服务器响应中已经设置了Content-Type为application/json //此时jQuery就能够自动把此处响应的内容解析成js对象数组 let container = document.querySelector('.container-right'); for(let blog of body){ //这个for循环就相当于Java里的for-each循环 //根据当前这个blog,构造出一个html片段 //构造的格式按上面注释部分来 //构造blog的div标签 let 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.href = 'blog_detail.html?blogId=' + blog.blogId; a.innerHTML = '查看全文 >>'; blogDiv.appendChild(a); //最后一步:将拼好的blogDiv添加到container container.appendChild(blogDiv); } } }) } getBlogs(); checkLogin(); </script> </body> </html>
(2)博客详情页blog_detail.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>博客详情页</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/blog_detail.css"> </head> <body> <!-- 导航栏. nav 是 导航 这个词的缩写 --> <div class="nav"> <!-- logo --> <img src="image/logo2.jpg" alt=""> <div class="title">我的博客系统</div> <!-- 只是一个空白, 用来把后面的链接挤过去 --> <!-- 这是一个简单粗暴的写法~~ --> <div class="spacer"></div> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <!-- 这里的地址回头再说 --> <a href="">注销</a> </div> <!-- 页面的主体部分 --> <div class="container"> <!-- 左侧信息 --> <div class="container-left"> <!-- 这个 div 表示整个用户信息的区域 --> <div class="card"> <!-- 用户的头像 --> <img src="image/kun.jpg" alt=""> <!-- 用户名 --> <h3>hlizoo</h3> <!-- github 地址 --> <a href="https://www.github.com">github 地址</a> <!-- 统计信息 --> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>1</span> </div> </div> </div> <!-- 右侧信息 --> <div class="container-right"> <h3></h3> <div class="date"></div> <div id="content"> </div> </div> </div> <!-- 引入 ajax 的依赖 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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> <!-- 引入 app.js,判断是否登录 --> <script src="js/app.js"></script> <script> function getBlog(){ $.ajax({ type:'get', url:'blog' + location.search, success:function(body){ //根据服务器返回的响应内容,构造页面 //第一步:构造标题 let h3 = document.querySelector('.container-right h3'); h3.innerHTML = body.title; //第二步:构造时间 let dateDiv = document.querySelector('.container-right .date'); dateDiv.innerHTML = body.postTime; //第三步:构造内容 //格式:editormd.markdownToHTML('content',{markdown:markdown}); editormd.markdownToHTML('content',{markdown:body.content}); } }); } getBlog(); checkLogin(); </script> </body> </html>
(3)博客编辑页blog_edit.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>博客编辑页</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/blog_edit.css"> <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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> </head> <body> <!-- 导航栏. nav 是 导航 这个词的缩写 --> <div class="nav"> <!-- logo --> <img src="image/logo2.jpg" alt=""> <div class="title">我的博客系统</div> <!-- 只是一个空白, 用来把后面的链接挤过去 --> <!-- 这是一个简单粗暴的写法~~ --> <div class="spacer"></div> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <!-- 这里的地址回头再说 --> <a href="">注销</a> </div> <!-- 博客编辑页的版心 --> <div class="blog-edit-container"> <form action=""> <!-- 标题编辑区 --> <div class="title"> <input type="text" id="title-input"> <input type="submit" id="submit"> </div> <!-- 博客编辑器 --> <!-- 把 md 编辑器放到这个 div 中 --> <div id="editor"> </div> </form> </div> <!-- 引入 app.js,判断是否登录 --> <script src="js/app.js"></script> <script> var editor = editormd("editor", { // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%", // 设定编辑器高度 height: "calc(100% - 50px)", // 编辑器中的初始内容 markdown: "# 在这里写下一篇博客", // 指定 editor.md 依赖的插件路径 path: "editor.md/lib/" }); checkLogin(); </script> </body> </html>
④效果展示
💚当你未登录时想访问博客编辑页面,就会自动跳转到博客登录页;具体效果要自行运行代码
(6)实现功能五
1.设计前后端交互接口
2.编写后端代码
🌟在api包下创建一个新的文件,取名为UserServlet
package api; import com.fasterxml.jackson.databind.ObjectMapper; import dao.Blog; import dao.BlogDao; import dao.User; import dao.UserDao; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; @WebServlet("/user") public class UserServlet extends HttpServlet { //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据 private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页 String blogId = req.getParameter("blogId"); if (blogId==null){ //如果blogId为空,说明query string为空,此时就是博客列表页 HttpSession session = req.getSession(false); if (session==null){ //session为空,说明没有创建会话 User user = new User(); String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); return; } //因为前面LoginServlet的session保存了user对象,这里我们直接获取即可 User user = (User) session.getAttribute("user"); if (user==null){ //user为空,说明没有该用户 user = new User(); String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); return; } //代码来到这,说明前两步都正确了,此时说明对话存在且拿到了一个user对象 String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); }else { //如果blogId不为空,说明query string不为空,此时就是博客详情页 //此时需要根据blogId去数据库查询对应的博客 BlogDao blogDao = new BlogDao(); Blog blog = blogDao.getblog(Integer.parseInt(blogId)); if (blog==null){ //表示没有查询到该博客,此时返回一个空的user对象 User user = new User(); String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); return; } UserDao userDao = new UserDao(); //根据博客的userId来查询该博客对应的用户信息,比如谁写的 User user = userDao.getUserById(blog.getUserId()); if (user==null){ //表示没有查询到该用户,此时返回一个空的user对象 user = new User(); String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); return; } //代码来到这,说明根据blogId已经查询到对应的用户了 String respJson = objectMapper.writeValueAsString(user); resp.setContentType("application/json charset=utf8"); resp.getWriter().write(respJson); } } }
3.编写前端代码
①使用VsCode打开博客列表页blog_list.html这个前端代码,添加一个新的方法,取名为getUser
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>博客列表页</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/blog_list.css"> </head> <body> <!-- 导航栏. nav 是 导航 这个词的缩写 --> <div class="nav"> <!-- logo --> <img src="image/logo2.jpg" alt=""> <div class="title">我的博客系统</div> <!-- 只是一个空白, 用来把后面的链接挤过去 --> <!-- 这是一个简单粗暴的写法~~ --> <div class="spacer"></div> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <!-- 这里的地址回头再说 --> <a href="">注销</a> </div> <!-- 页面的主体部分 --> <div class="container"> <!-- 左侧信息 --> <div class="container-left"> <!-- 这个 div 表示整个用户信息的区域 --> <div class="card"> <!-- 用户的头像 --> <img src="image/kun.jpg" alt=""> <!-- 用户名 --> <h3>hlizoo</h3> <!-- github 地址 --> <a href="https://www.github.com">github 地址</a> <!-- 统计信息 --> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>1</span> </div> </div> </div> <!-- 右侧信息 --> <div class="container-right"> <!-- <div class="blog"> <div class="title">我的第一篇博客博客博客博客</div> <div class="date">2023-05-11 20:00:00</div> <div class="desc"> 从今天起, 我要认真敲代码. Lorem ipsum dolor sit amet consectetur, adipisicing elit. Debitis repellendus voluptatum, reiciendis rem consectetur incidunt aspernatur eveniet excepturi magni quis sint, provident est at et pariatur dolorem aliquid fugit voluptatem. </div> <a href="blog_detail.html?blogId=1">查看全文 >> </a> </div> --> </div> </div> </div> <!-- 引入 ajax 的依赖 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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> <!-- 引入 app.js,判断是否登录 --> <script src="js/app.js"></script> <script> //编写JS代码 //构造ajax请求向服务器获取到博客列表数据,然后接收服务器返回的响应并展示到页面上 function getBlogs(){ $.ajax({ type:'get', url:'blog', success:function(body){ //在这里根据响应的内容,构造出html片段,展示到页面上 //由于在服务器响应中已经设置了Content-Type为application/json //此时jQuery就能够自动把此处响应的内容解析成js对象数组 let container = document.querySelector('.container-right'); for(let blog of body){ //这个for循环就相当于Java里的for-each循环 //根据当前这个blog,构造出一个html片段 //构造的格式按上面注释部分来 //构造blog的div标签 let 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.href = 'blog_detail.html?blogId=' + blog.blogId; a.innerHTML = '查看全文 >>'; blogDiv.appendChild(a); //最后一步:将拼好的blogDiv添加到container container.appendChild(blogDiv); } } }) } getBlogs(); checkLogin(); function getUser(){ $.ajax({ type:'get', url:'user', success:function(body){ //body就是解析后的user对象 //此时我们构造的格式按照上述代码-左侧信息那部分 //构造用户名 let h3 = document.querySelector(".card h3"); h3.innerHTML = body.username; } }) } getUser(); </script> </body> </html>
②使用VsCode打开博客详情页blog_detail.html这个前端代码,添加一个新的方法,取名为getUser
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>博客详情页</title> <link rel="stylesheet" href="css/common.css"> <link rel="stylesheet" href="css/blog_detail.css"> </head> <body> <!-- 导航栏. nav 是 导航 这个词的缩写 --> <div class="nav"> <!-- logo --> <img src="image/logo2.jpg" alt=""> <div class="title">我的博客系统</div> <!-- 只是一个空白, 用来把后面的链接挤过去 --> <!-- 这是一个简单粗暴的写法~~ --> <div class="spacer"></div> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <!-- 这里的地址回头再说 --> <a href="">注销</a> </div> <!-- 页面的主体部分 --> <div class="container"> <!-- 左侧信息 --> <div class="container-left"> <!-- 这个 div 表示整个用户信息的区域 --> <div class="card"> <!-- 用户的头像 --> <img src="image/kun.jpg" alt=""> <!-- 用户名 --> <h3>hlizoo</h3> <!-- github 地址 --> <a href="https://www.github.com">github 地址</a> <!-- 统计信息 --> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>1</span> </div> </div> </div> <!-- 右侧信息 --> <div class="container-right"> <h3></h3> <div class="date"></div> <div id="content"> </div> </div> </div> <!-- 引入 ajax 的依赖 --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <!-- 引入 editor.md 的依赖 --> <link rel="stylesheet" href="editor.md/css/editormd.min.css" /> <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> <!-- 引入 app.js,判断是否登录 --> <script src="js/app.js"></script> <script> function getBlog(){ $.ajax({ type:'get', url:'blog' + location.search, success:function(body){ //根据服务器返回的响应内容,构造页面 //第一步:构造标题 let h3 = document.querySelector('.container-right h3'); h3.innerHTML = body.title; //第二步:构造时间 let dateDiv = document.querySelector('.container-right .date'); dateDiv.innerHTML = body.postTime; //第三步:构造内容 //格式:editormd.markdownToHTML('content',{markdown:markdown}); editormd.markdownToHTML('content',{markdown:body.content}); } }); } getBlog(); checkLogin(); function getUser(){ $.ajax({ type:'get', url:'user' + location.search, success:function(body){ //body就是解析后的user对象 //此时我们构造的格式按照上述代码-左侧信息那部分 //构造用户名 let h3 = document.querySelector(".card h3"); h3.innerHTML = body.username; } }) } getUser(); </script> </body> </html>
③效果展示-博客列表页
④效果展示-博客详情页
(7)实现功能六
💗思路与步骤:
1.设计前后端交互接口
2.编写后端代码
package api; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; @WebServlet("/logout") public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { HttpSession session = req.getSession(false); if (session==null){ //此时会话为空,就证明还没登录,此时也就没有什么退出登录可言 //让它直接转回到用户登录页面 resp.sendRedirect("login.html"); return; } //之前在LoginServlet中当用户登录成功时,我们会在sessin中保存user对象 //此时就可以直接删除会话中的user session.removeAttribute("user"); resp.sendRedirect("login.html"); } }
3.编写前端代码
①可以看到页面的右上角有一个“注销”
②使用VsCode打开blog_list.html、blog_edit.html、blog_detail.html这几个前端代码,找到下图所示部分
③这几个代码分别修改<a href>的部分,很简单,只需要加个logout
④代码分析
(8)实现功能七
💗思路与步骤:
这里的过程本质上和登录类似
核心都是通过form表单,把页面中用户输入的内容,给提交到服务器这边
服务器调用insert方法插入一条数据将内容保存到数据库中
1.设计前后端交互接口
2.编写后端代码
💗在BlogServlet文件中添加doPost方法的代码
package api; import com.fasterxml.jackson.databind.ObjectMapper; import dao.Blog; import dao.BlogDao; import dao.User; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import java.io.IOException; import java.util.List; //因为上述前后端交互接口中我们已经约定了方法是GET,路径是blog,因此这里我们得遵守约定 @WebServlet("/blog") public class BlogServlet extends HttpServlet { //定义ObjectMapper对象,这是jackson库,我们用它来解析或者组织JSON格式数据 private ObjectMapper objectMapper = new ObjectMapper(); @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //先获取一下blogId的value值,根据能否获取判断是博客列表页还是博客详情页 String blogId = req.getParameter("blogId"); BlogDao blogDao = new BlogDao(); if (blogId==null){ //如果blogId为空,说明query string为空,此时就是博客列表页 //此处的工作:查询数据库,获取到数据库中的所有博客数据之后,构造成要求的json格式并返回 List<Blog> blogs = blogDao.getBlogs(); //此处就会把博客列表blogs构造成json格式 String respJson = objectMapper.writeValueAsString(blogs); //写到响应并返回 resp.setContentType("application/json;charset=utf8"); resp.getWriter().write(respJson); }else { //如果blogId不为空,说明query string不为空,此时就是博客详情页 //根据不同的blogId查看到不同的blog对象和数据 //注意:这里是单个博客,而不是所有博客 Blog blog = blogDao.getblog(Integer.parseInt(blogId)); if (blog==null){ //如果blog为空,也就是blogId不存在没有这篇博客 //就返回一个blogId为0的blog对象,因为blogId不可能为0,此时再由前端来进行判定 blog=new Blog(); } //将指定的博客数据构造成json格式 String respJson = objectMapper.writeValueAsString(blog); //写到响应并返回 resp.setContentType("application/json;charset=utf8"); resp.getWriter().write(respJson); } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //第一步:先去获取登录的用户 //这里其实不用判断session和user是否为空;因为在博客编辑页已经做了登录的检查,如果你没有登录是进不去博客编辑页的 HttpSession session = req.getSession(false); User user = (User) session.getAttribute("user"); //第二步:获取请求中传递过来的内容(内容包括题目和内容) req.setCharacterEncoding("utf8"); String title = req.getParameter("title"); String content = req.getParameter("content"); if (title==null || content==null || "".equals(title) || "".equals(content)){ resp.setContentType("text/html ; charset=utf8"); resp.getWriter().write("标题或者正文内容为空"); return; } //第三步:构造Blog对象,添加到数据库中保存 //至于构造时间,因为在BlogDao中的insert方法中已经使用了SQL中自带的now()方法获取当前时间,这里我们可以不用自己设置 Blog blog = new Blog(); blog.setTitle(title); blog.setContent(content); blog.setUserId(user.getUserId()); //将博客插入到数据库中 BlogDao blogDao = new BlogDao(); blogDao.insert(blog); //第四步:跳转到博客列表页 resp.sendRedirect("blog_list.html"); } }
3.编写前端代码
①主要工作就是把form表单给补齐
②使用VsCode打开blog_edit.html代码,找到下图所示部分
③修改代码
<!-- 博客编辑页的版心 --> <div class="blog-edit-container"> <form action="blog" method="post"> <!-- 标题编辑区 --> <div class="title"> <input type="text" id="title-input" name="title"> <input type="submit" id="submit"> </div> <!-- 博客编辑器 --> <!-- 把 md 编辑器放到这个 div 中 --> <div id="editor"> <textarea name="content" style="display:none;"></textarea> </div> </form> </div>
④代码分析
⑤效果展示