JavaWeb项目—— 博客系统

article/2025/9/11 17:36:17

文章目录

  • 效果展示
    • 1. 创建 maven 项目
    • 2. 设计数据库
    • 3. 封装数据库的操作代码
      • 3.1 创建 DBUtil 类
      • 3.2 创建 Blog(代表一篇博客)
      • 3.3 创建 User(代表一个用户)
      • 3.4 创建类 BlogDao(对博客表进行操作)
      • 3.5 创建类 UserDao (对用户表进行操作)
    • 4. 导入之前已经写好的前端代码
    • 5. 实现博客主页界面
      • 5.1 约定好前后端交互接口
      • 5.2 实现 BlogServlet
      • 5.3 实现 前端代码
    • 6. 实现博客详情界面
      • 6.1 约定好前后端交互接口
      • 6.2 实现BlogServlet
      • 6.3 实现前端代码
    • 7. 实现登录界面
      • 7.1 约定好前后端交互接口
      • 7.2 实现 LoginServlet
      • 7.3 实现前端代码
    • 8. 实现登录状态判定功能
      • 8.1 约定前后端交互接口
      • 8.2 在 LoginServlet 进行代码添加
      • 8.3 在前端代码中创建 common.js
      • 8.4 修改前端代码
    • 9. 实现显示用户信息功能
      • 9.1 约定好前后端交互接口
      • 9.2 实现 AuthorServlet 代码
      • 9.3 实现前端代码
        • 针对博客列表页进行修改
        • 针对博客详情页
    • 10. 实现注销功能
      • 10.1 约定好前后端交互接口
      • 10.2 实现 LogouServlet
      • 10.3 实现前端代码
    • 11. 实现发布博客功能
      • 11.1 约定好前后端交互的接口
      • 11.2 在BlogServlet中添加doPost方法
      • 11.3 实现前端代码
    • 12. 删除博客
      • 12.1 约定号前后端交互接口
      • 12.1 实现前端代码
      • 12.2 实现BlogDeleteServlet

效果展示

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


1. 创建 maven 项目

创建必要的目录,引入需要的依赖

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2. 设计数据库

本系统要存入博客文章的信息用户的信息

创建博客表:

博客的 id,博客的标题,博客的内容,博客的日期,博文的博主 id

创建用户表:

用户 id 用户名 用户密码

-- 创建一个数据库
create database if not exists java102_blog;use java102_blog;-- 创建一个博客表.
drop table if exists blog;
create table blog (blogId int primary key auto_increment,title varchar(1024),content mediumtext,userId int,         -- 文章作者的 idpostTime datetime   -- 发布时间
);---- 给博客表中插入点数据, 方便测试.
insert into blog values(null, '这是第一篇博客', '从今天开始, 我要认真学 Java', 1, now());
insert into blog values(null, '这是第二篇博客', '从昨天开始, 我要认真学 Java', 1, now());
insert into blog values(null, '这是第三篇博客', '从前天开始, 我要认真学 Java', 1, now());
insert into blog values(null, '这是第一篇博客', '从今天开始, 我要认真学 C++', 2, now());
insert into blog values(null, '这是第二篇博客', '从昨天开始, 我要认真学 C++', 2, now());
insert into blog values(null, '这是第三篇博客', '# 一级标题\n ### 三级标题\n > 这是引用内容', 2, now());-- 创建一个用户表
drop table if exists user;
create table user (userId int primary key auto_increment,username varchar(128) unique,    -- 后续会使用用户名进行登录, 一般用于登录的用户名都是不能重复的.password varchar(128)
);insert into user values(null, 'zhangsan', '123');
insert into user values(null, 'lisi', '123');
insert into user values(null, 'ling', '123');

3. 封装数据库的操作代码

创建包 model 用来存放数据库的代码

3.1 创建 DBUtil 类

用于和数据库建立连接

package model;import com.mysql.cj.jdbc.MysqlDataSource;import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/***这个类 封装 和 建立 数据库 连接 的 操作*/
public class DBUtil {private static final String URL = "jdbc:mysql://127.0.0.1:3306/java102_blog?characterEncoding=utf8&useSSL=false";private static final String USERNAME = "root";private static final String PASSWORD = "707703";private volatile static DataSource dataSource = null;private static DataSource getDataSource() {if(dataSource == null) {synchronized (DBUtil.class) {if(dataSource == null) {dataSource = new MysqlDataSource();((MysqlDataSource)dataSource).setUrl(URL);((MysqlDataSource)dataSource).setUser(USERNAME);((MysqlDataSource)dataSource).setPassword(PASSWORD);}}}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 throwables) {throwables.printStackTrace();}}if(statement != null) {try {statement.close();} catch (SQLException throwables) {throwables.printStackTrace();}}if(connection != null) {try {connection.close();} catch (SQLException throwables) {throwables.printStackTrace();}}}
}

3.2 创建 Blog(代表一篇博客)

package model;import java.sql.Timestamp;
import java.text.SimpleDateFormat;// 每个 blog对象 ,对应 blog 表里的一条记录
public class Blog {private int blogId;private String title;private String content;private int userId;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;}// 把这里 的 getter 方法给改了,不是返回一个 时间搓对象,而是返回一个 string(格式化的时间)public String getPostTime() {// 使用这个类 来完成时间戳到格式化日期的转换// 这个 转换过程,需要构造方法中制定要转换的格式,然后调用 format 来进行转换SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}
}

3.3 创建 User(代表一个用户)

package model;// 每个 model.User 对象, 期望能够表示 user 表中的一条记录.
public class User {private int userId = 0;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;}
}

3.4 创建类 BlogDao(对博客表进行操作)

注意:
对数据进行插入删除操作时,执行 sql:
在这里插入图片描述
其他操作:
在这里插入图片描述
插入顺序:
在这里插入图片描述

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;/*** 这个类用于去封装博客表的 基本操作*/
public class BlogDao {// 1.往博客表里,插入一个博客public void insert(Blog blog) {// JDBC 代码Connection connection = null;PreparedStatement statement = null;try {// 1. 先和数据库建立连接connection = DBUtil.getConnection();// 2. 构造 sql 语句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 throwables) {throwables.printStackTrace();}finally {// 4. 关闭连接,释放资源DBUtil.close(connection,statement,null);}}// 2. 获取博客列表中的所有博客信息public List<Blog> selectAll() {List<Blog> blogs = new ArrayList<>();// JDBC 代码Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {// 1. 先建立链接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "select * from blog order by postTime desc";statement = connection.prepareStatement(sql);// 3. 执行 sqlresultSet = statement.executeQuery();// 把查询到的数据 存储到 blogs 当中while (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));// 这里需要针对内容进行截断(太长,就去掉后面的)String content = resultSet.getString("content");if(content.length() > 50) {content = content.substring(0,50) + "..点进来吧我的宝";}blog.setContent(content);blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return blogs;}// 3. 能够根据博客 id 获取到制定的博客内容(用于博客详情页)public Blog selectOne(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. 执行 sqlresultSet = statement.executeQuery();// 此处我们是使用 主键 来作为 查询条件,查询结果,要么是 1,要么是 0if(resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));return blog;}} catch (SQLException throwables) {throwables.printStackTrace();}finally {// 4. 关闭连接 释放资源DBUtil.close(connection,statement,resultSet);}return null;}// 4.  从博客列表中,根据博客 id 删除博客public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {// 1. 建立 连接connection = DBUtil.getConnection();// 2. 构造 sql 语句String sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);// 3. 执行 sqlstatement.executeUpdate();} catch (SQLException throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,null);}}
}

3.5 创建类 UserDao (对用户表进行操作)

package model;import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;/*** 这个类用于 封装 用户表的 基本操作*/
public class UserDao {// 1. 根据用户名 查找用户信息public User selectByName(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. 执行 sqlresultSet = 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 throwables) {throwables.printStackTrace();}finally {// 4. 关闭连接 释放资源DBUtil.close(connection,statement,resultSet);}return null;}// 2. 根据 用户 id 来找 用户信息// 博客详情页 就可以根据用户 id 来查询作者的名字,把作者名字显示出来public User selectById(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. 执行sqlresultSet = statement.executeQuery();// 此处的 username 使用 unique 约束,要么能查到一个,要么一个都查不到// 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 throwables) {throwables.printStackTrace();}finally {DBUtil.close(connection,statement,resultSet);}return null;}
}

4. 导入之前已经写好的前端代码

在这里插入图片描述

5. 实现博客主页界面

5.1 约定好前后端交互接口

在这里插入图片描述

5.2 实现 BlogServlet

import model.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;// 通过这个类, 来处理 /blog 路径对应的请求
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();// 这个方法用来获取到数据库中的博客列表.@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 从数据库中查询到博客列表, 转成 JSON 格式, 然后直接返回即可.BlogDao blogDao = new BlogDao();List<Blog> blogs = blogDao.selectAll();//把 blogs 对象转成 JSON 格式.String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}
}

5.3 实现 前端代码

在 blog_list.html 中 实现 ajax

注意:引入依赖

<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script><script>// 在页面加载的时候, 通过 ajax 给服务器发送数据, 获取到博客列表信息, 并且显示在界面上. function getBlogList() {$.ajax({type: 'get',url: 'blog',success: function(body) {// 获取到的 body 就是一个 js 对象数组, 每个元素就是一个 js 对象, 根据这个对象构造 div// 1. 先把 .right 里原有的内容给清空let rightDiv = document.querySelector('.right');rightDiv.innerHTML = '';// 2. 遍历 body, 构造出一个个的 blogDivfor (let blog of body) {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.innerHTML = '查看全文 &gt;&gt;';// 此处希望点击之后能够跳转到 博客详情页 !!// 这个跳转过程需要告知服务器要访问的是哪个博客的详情页. a.href = 'blog_detil.html?blogId=' + blog.blogId;blogDiv.appendChild(a);// 把 blogDiv 挂到 dom 树上!rightDiv.appendChild(blogDiv);}}, error: function() {alert("获取博客列表失败!");}});}getBlogList();</script>

6. 实现博客详情界面

6.1 约定好前后端交互接口

在这里插入图片描述

6.2 实现BlogServlet

这里后端代码和博客列表页的获取,基本相同,就直接放到一个方法中,来实现!使用blogId参数来区别是获取博客列表还是详情
这里注意:博客列表页在也在BlogServlet中实现的,我们如何去区别呢?:
在这里插入图片描述

package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.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;/*** // doGet 获取博客列表页*/
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");BlogDao blogDao = new BlogDao();// 先尝试获取到 req 中的 blogId 参数. 如果该参数存在, 说明是要请求博客详情// 如果该参数不存在, 说明是要请求博客的列表.String param = req.getParameter("blogId");if (param == null) {// 不存在参数, 获取博客列表List<Blog> blogs = blogDao.selectAll();// 把 blogs 对象转成 JSON 格式.String respJson = objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {// 存在参数, 获取博客详情int blogId = Integer.parseInt(param);Blog blog = blogDao.selectOne(blogId);String respJson = objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}
}

6.3 实现前端代码

修改 blog_detail.html,让这个页面加载的时候,能够调用到上述的接口,来从服务器获取到博客数据

注意:
在这里插入图片描述

在这里插入图片描述

在博客详情页中引入 editor


<script>function getBlogDetail() {$.ajax({type: 'get',// location.search 拿到了形如 '?blogId=5' 这样的一段内容url: 'blog2' + location.search,success: function(body) {// 根据 body 中的内容来构造页面// 1. 构造博客标题Slet h3 = document.querySelector(".blog-content>h3");h3.innerHTML = body.title;// 2. 构造博客发布时间let dateDiv = document.querySelector('.date');dateDiv.innerHTML = body.postTime;// 3. 构造博客正文// 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串// 咱们需要的是渲染后的, 带有格式的效果// let content = document.querySelector('#content');// content.innerHTML = body.content;// 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下. editormd.markdownToHTML('content', {markdown: body.content});}});}getBlogDetail();</script>

7. 实现登录界面

这里需要注意:啥样才算是已经登录

用户有一个 session,同时 session 有一个 user 属性 ~
两者同时具备,才叫登录状态

这里先给注销做铺垫:理解一下注销是如何操作的:

注销只要破坏掉上面的任意一个条件就行了

7.1 约定好前后端交互接口

在这里插入图片描述

7.2 实现 LoginServlet

package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.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 {private  ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setCharacterEncoding("utf-8");// 1. 获取到请求中的参数String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || "".equals(username) || password == null || "".equals(password)) {// 请求内容缺失,登录失败resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前的用户名或者密码为空");return;}// 2. 和数据库中的的内容进行比较UserDao userDao = new UserDao();User user = userDao.selectByName(username);if(user == null || !user.getPassword().equals(password)) {// 用户没有查到或者密码不匹配,登录失败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.3 实现前端代码

修改blog_login.html 这里只需要在原来登录操作上套上一层 form 标签就可以了
在这里插入图片描述

因为这里在原有的基础上填上了 form表单,我们在 css 中也会有一些改动

在这里插入图片描述

<!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_login.css">
</head>
<body><!--这是导航栏 nav--><div class = "nav"><img src="imge/ph.png" alt=""><!--标题 span--><span>我的博客系统</span><!--这是一个空白元素用来占位置--><div class="spacer"></div><!--链接标签<a>--><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a><!--# 空链接--> <!--注销没必要显示在登录页面-->><!-- <a href="#">注销</a> --></div><div class="login-container"><form action="login" method="post"><!--空白页--><div class="login-dialog"><h3>登录</h3><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"><!-- <button>提交</button> --><input type="submit" id="submit" value="提交"></div></div></form></div></div>
</body>
</html>

8. 实现登录状态判定功能

在这里插入图片描述

8.1 约定前后端交互接口

在这里插入图片描述

8.2 在 LoginServlet 进行代码添加

package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.User;
import model.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 {private  ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf-8");resp.setCharacterEncoding("utf-8");// 1. 获取到请求中的参数String username = req.getParameter("username");String password = req.getParameter("password");if(username == null || "".equals(username) || password == null || "".equals(password)) {// 请求内容缺失,登录失败resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前的用户名或者密码为空");return;}// 2. 和数据库中的的内容进行比较UserDao userDao = new UserDao();User user = userDao.selectByName(username);if(user == null || !user.getPassword().equals(password)) {// 用户没有查到或者密码不匹配,登录失败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");}//   //  这个方法用来让前端检测当前的登录状态~~~~~~~~~~~~~~~~~~~~~~// 1)检测有无会话 2)检测有无属性@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json;charset=utf8");HttpSession session =  req.getSession(false);if(session == null) {// 1) 检测有无会话,无说明未登录// 创建对象--> 里面为空User user = new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}User user = (User) session.getAttribute("user");if(user == null) {// 2)虽然有会话,但是会话里如果没有 user 对象,也视为未登录user = new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}// 已经登录的状态!// 注意,此处不要把密码返回到前端user.setPassword("");resp.getWriter().write(objectMapper.writeValueAsString(user));}
}

8.3 在前端代码中创建 common.js

在这里插入图片描述

// 这个文件里放一些 页面公共的代码
// 加上一个逻辑,通过 GET/login 这个接口来获取下当前的登录状态
function getUserInfo() {$.ajax({type: 'get',url: 'login',success: function(body) {// 判定此处的 body 是不是一个有效的 user 对象(userId 是否非 0)if(body.userId && body.userId >0) {// 登录成功// 不做处理console.log("当前用户登录成功!用户名:" + body.username);}else{// 登录失败// 让前端页面,跳转到 login.htmlalert("当前你尚未登录!请登录后再访问博客列表!");location.assign('blog_login.html');}},error: function() {alert("当前你尚未登录!请登录后再访问博客列表!");location.assign('blog_login.html');}});}
getUserInfo();

8.4 修改前端代码

blog_list.html blog_detil.html 中引入 js文件,就可以执行到里面的代码,也就进行了登录状态的监测了

 <script src="js/commom.js"></script>

在这里插入图片描述
在这里插入图片描述

9. 实现显示用户信息功能

9.1 约定好前后端交互接口

9.2 实现 AuthorServlet 代码

package controller;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;
import model.User;
import model.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 java.io.IOException;/*** 根据博客 id 查 博客信息* 根据 博客信息里的 作者 id 查 作者信息*/
@WebServlet("/authorInfo")
public class AuthorServlet  extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");// 通过这个方法, 来获取到指定的博客的作者信息.String param = req.getParameter("blogId");if (param == null || "".equals(param)) {// 参数缺少了.resp.getWriter().write("{ \"ok\": false, \"reason\": \"参数缺失!\" }");return;}// 根据当前 blogId 在数据库中进行查找, 找到对应的 Blog 对象, 再进一步的根据 blog 对象, 找到作者信息.BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(Integer.parseInt(param));if (blog == null) {resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的博客不存在!\" }");return;}// 根据 blog 对象, 查询到用户对象UserDao userDao = new UserDao();User author = userDao.selectById(blog.getUserId());if (author == null) {resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的用户不存在!\" }");return;}// 把 author 返回到浏览器这边// 注意要把密码给干掉!author.setPassword("");resp.getWriter().write(objectMapper.writeValueAsString(author));}
}

9.3 实现前端代码

在这里插入图片描述

针对博客列表页进行修改

在 common.js 中 修改代码
在这里插入图片描述

针对博客详情页


10. 实现注销功能

在导航栏中安排一个“注销”按钮,当用户点击注销之后,就会在服务器上取消登录状态,并且能够跳转到登录页面

10.1 约定好前后端交互接口

在这里插入图片描述

10.2 实现 LogouServlet

package controller;import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.*;
import java.io.IOException;
import java.util.Enumeration;@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.getWriter().write("当前用户尚未登录! 无法注销!");return;}// 然后把这个用户的会话中的信息给删掉就行了!!session.removeAttribute("user");resp.sendRedirect("blog_login.html");}
}

10.3 实现前端代码

将 blog_detail.html blog_list.html bloh_edit.htm中的注销标签进行修改

在这里插入图片描述

11. 实现发布博客功能

博客编辑页中,当用户输入博客标题,和正文之后,点击发布
此时就会把博客数据提交到服务器,由服务器存储到数据中

11.1 约定好前后端交互的接口

在这里插入图片描述

11.2 在BlogServlet中添加doPost方法

@Overrideprotected void doPost(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;}// 一定要先指定好请求按照哪种编码来解析req.setCharacterEncoding("utf8");// 先从请求中, 取出参数(博客的标题和正文)String title = req.getParameter("title");String content = req.getParameter("content");if (title == null || "".equals(title) || content == null || "".equals(content)) {// 直接告诉客户端, 请求参数不对resp.setContentType("text/html;charset=utf8");resp.getWriter().write("提交博客失败! 缺少必要的参数!");return;}// 构造 Blog 对象, 把当前的信息填进去, 并插入数据库中// 此处要给 Blog 设置的属性, 主要是 title, content, userId (作者信息)// postTime 和 blogId 都不需要手动指定, 都是插入数据库的时候自动生成的.Blog blog = new Blog();blog.setTitle(title);blog.setContent(content);// 作者 id 就是当前提交这个博客的用户的身份信息!!blog.setUserId(user.getUserId());BlogDao blogDao = new BlogDao();blogDao.insert(blog);// 重定向到, 博客列表页!resp.sendRedirect("blog_list.html");}

11.3 实现前端代码

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

<!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"><!-- 引入 editor.md 的依赖 --><link rel="stylesheet" href="editor.md/css/editormd.min.css" /><script src="js/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.min.js"></script>
</head>
<body><!-- 这是导航栏 --><div class="nav"><img src="imge/ph.png" alt=""><span>我的博客系统</span><!-- 空白元素, 用来占位置 --><div class="spacer"></div><a href="blog_list.html">主页</a><a href="blog_edit.html">写博客</a><a href="logout">注销</a></div><!-- 包裹整个博客编辑页内容的顶级容器 --><div class="blog-edit-container"><form action="blog" method="post" style="height: 100%"><div class="title"><input type="text" placeholder="在此处输入标题" name="title"><!-- <button>发布文章</button> --><input type="submit" value="发布文章" id="submit"></div><!-- 放置 md 编辑器 --><div id="editor"><!-- 为了进行 form的提交,此处搞一下 textarea 多行编辑框,借助这个编辑框来实现表单的提交--><textarea name="content" style="display: none"></textarea></div></form></div><script>// 初始化编辑器let editor = editormd("editor", {// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%",// 设定编辑器高度height: "calc(100% - 50px)",// 编辑器中的初始内容markdown: "# 在这里写下一篇博客",// 指定 editor.md 依赖的插件路径path: "editor.md/lib/",// 此处要加上一个重要的选项,然后 editr.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea中saveHTMLToTextarea:true,});</script>
</body>
</html>

12. 删除博客

在这里插入图片描述

12.1 约定号前后端交互接口

在这里插入图片描述

12.1 实现前端代码

 <script>function getBlogDetail() {$.ajax({type: 'get',// location.search 拿到了形如 '?blogId=5' 这样的一段内容url: 'blog' + location.search,success: function(body) {// 根据 body 中的内容来构造页面// 1. 构造博客标题let h3 = document.querySelector(".blog-content>h3");h3.innerHTML = body.title;// 2. 构造博客发布时间let dateDiv = document.querySelector('.date');dateDiv.innerHTML = body.postTime;// 3. 构造博客正文// 如果直接把 content 设为 innerHTML, 此时展示在界面上的内容, 是原始的 markdown 字符串// 咱们需要的是渲染后的, 带有格式的效果// let content = document.querySelector('#content');// content.innerHTML = body.content;// 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下. editormd.markdownToHTML('content', {markdown: body.content});}});}   // 加上一个逻辑,通过 GET /login 这个接口来获取下当前的登录状态          function getUserInfo(pageName) {$.ajax({type:'get',url: 'login',success: function(body) {// 判断此处的 body 是不是一个有效的user 对象(userId 是否为0)if(body.userId && body.userId > 0) {// 登录成功// 不做处理console.log("当前用户登录成功!用户名:" + body.username);// 在getUserInfo 的回调函数中,来调用获取用户信息getAuthorInfo(body);}else {// 登录失败// 让前端页面,跳转到 login.htmlalert("当前您尚未登录!请登录后再访问博客列表");location.assign('blog_login.html');}},error: function() {alert("当前您尚未登录!请登录后再访问博客列表");location.assign('blog_login.html');}});}// 判定用户的登录状态getUserInfo('blog_detail.html');// 从服务器获取一下当前博客的作者信息, 并显示到界面上. // 参数 user 就是刚才从服务器拿到的当前登录用户的信息function getAuthorInfo(user) {$.ajax({type: 'get',url: 'authorInfo' + location.search,success: function(body) {// 此处的body,就是服务器返回的User对象,是文章作者信息if(body.username) {getBlogDetail();changeUserName(body.username);if(body.username == user.username ) {// 作业和登录的用户是一个人,则显示 删除按钮let navDiv = document.querySelector('.nav');let a = document.createElement('a');a.innerHTML = '删除';// 点击删除,构造一个 形如 blogDelete?blogId = 6 这样的请求a.href = 'blogDelete' + location.search;navDiv.appendChild(a);}}else{console.log("获取信息失败" + body.reason);}}});}function changeUserName(username) {let h3 = document.querySelector('.card>h3');h3.innerHTML = username;}     </script>

12.2 实现BlogDeleteServlet

package controller;import model.Blog;
import model.BlogDao;
import model.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;/*** 实现删除功能*/
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 1. 检查当前用户是否登录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;}// 2. 获取到参数中的 blogIdString blogId = req.getParameter("blogId");if (blogId == null || "".equals(blogId)) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前 blogId 参数不对!");return;}// 3. 获取要删除的博客信息.BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectOne(Integer.parseInt(blogId));if (blog == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前要删除的博客不存在!");return;}// 4. 再次校验, 当前的用户是否就是博客的作者if (user.getUserId() != blog.getUserId()) {// 这一点在前端这里其实也处理过~~ 但是此处还是再校验一次, 不是坏事!!!resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前登录的用户不是作者, 没有权限删除!");return;}// 5. 确认无误, 开始删除blogDao.delete(Integer.parseInt(blogId));// 6. 重定向到博客列表页resp.sendRedirect("blog_list.html");}}

http://chatgpt.dhexx.cn/article/r7U4ZoqZ.shtml

相关文章

Java个人博客项目

目录 引言 开发环境 所用技术 项目目录 数据库设计 功能介绍 源码下载 更多优质练手项目 Java学生成绩管理系统 JavaWeb练手项目ATM机 Java图书管理系统 Java电子发票管理系统 引言 这个项目是大二下学期期末考核项目&#xff0c;当时是自选主题写一个项目&#x…

Java项目——个人博客系统

文章目录 一、项目背景二、项目功能三、项目的基本流程1.准备工作2.数据库设计3.准备前端页面4.实现前端匹配的Servlet所需功能5.项目难点 一、项目背景 在学习完JavaWeb相关知识后&#xff0c;有了基础能力就想通过完成一个Javaweb项目来回顾和加强已经学过的知识&#xff0c…

【Java】博客系统——详细解释+代码+详细注释(课设必过)

目录 前言 博客系统简要分析 一、数据库的设计 1.1 分析 1.2 代码实现&#xff08;创建数据库和表&#xff09; 二、封装数据库&#xff08;JDBC代码的编写&#xff09; 2.1、首先通过创建Maven项目&#xff0c;基于Small Tomcat部署 servlet&#xff1b; 2.2、封装数据…

SSM分布式框架搭建

现在我们看一下我们大家的项目的结构&#xff1a; 一、建立框架&#xff1a; 二、建立root File –》 New –》Module 三、建立子项目tool File–》New –》Module 以同样的方式建立base-manage项目&#xff0c;他的子项目选择org.apache.maven.archetypes:maven-archetype…

8.分布式爬虫框架

目录 分布式爬虫框架消息队列Redis和Scrapy-Redis 分布式爬虫框架 分布式爬虫框架分为两种&#xff1a;控制模式&#xff08;左&#xff09;和自由模式&#xff08;右&#xff09;&#xff1a; 控制模式中的控制节点是系统实现中的瓶颈&#xff0c;自由模式则面临爬行节点之间…

golang 分布式框架Origin学习笔记

最近项目的后端需求是全球同服的&#xff0c;在使用语言方面确定了为golang之后&#xff0c;了解了一下当前的一些goalng游戏服务器框架&#xff0c;终于在leaf/pitaya/ 等众多框架中选择了 Origin&#xff0c; 主要是因为它是分布式框架&#xff0c;微服务架构&#xff0c;比较…

Java开发Dubbo分布式框架

引言 Dubbo是一个分布式服务框架&#xff0c;致力于提供高性能和透明化的RPC远程服务调用方案&#xff0c;以及SOA服务治理方案。简单的说&#xff0c;dubbo就是个服务框架&#xff0c;如果没有分布式的需求&#xff0c;其实是不需要用的&#xff0c;只有在分布式的时候&#x…

PyTorch 分布式框架 Ray :保姆级入门教程

来源&#xff1a;官方博客 翻译&#xff1a;PyTorch 开发者社区&#xff08;微信公众号&#xff09; 今天的机器学习需要分布式计算。无论是训练网络、调整超参数、服务模型还是处理数据&#xff0c;机器学习都是计算密集型的&#xff0c;如果没有访问集群&#xff0c;速度会非…

celery 分布式框架详解

Celery 结构图 如果没有celery&#xff0c;让你自己设计一个异步任务队列你怎么做。 首先&#xff0c;要有一个发起任务的client&#xff0c;选定一定保存任务信息的媒介&#xff0c;由一个worker去一直监听这个信息媒介&#xff0c;这个worker最好是多进程的&#xff0c;另外…

分布式定时任务调度框架

分布式定时任务调度框架 1 分布式定时任务框架设计 1.1 所需的功能 一个分布式定时任务&#xff0c;需要具备有以下几点功能&#xff1a; 1&#xff09;核心功能&#xff1a;定时调度、任务管理、可观测日志 2&#xff09;高可用&#xff1a;集群、分片、失败处理 3&#xf…

# 手把手教学超详细python通用爬虫分布式框架(一)

手把手教学超详细python通用爬虫分布式框架(一) 这里日后添加系列文章的所有文章的目录 文章目录 手把手教学超详细python通用爬虫分布式框架(一)前言一、所谓任务&#xff1f;二、任务需要什么1.启动格式2.任务执行流程分析3.任务灵活化 总结 前言 采用 vueflask&#xff0c;…

使用c++开发分布式框架实践

目前比较流行的分布式框架有dubbo&#xff0c;springcloud&#xff0c;JMX等&#xff0c;都是java实现的&#xff0c;但是在做c项目时&#xff0c;也有分布式的需求&#xff0c;这时我们可以基于grpc自己设计一套分布式框架。 整体思路如下&#xff1a; Consumer服务消费者&…

Ray入门指南——分布式框架(待更新)

1. ray库介绍 金融、工程模型需要大量使用 Pandas 和 Numpy 来做矩阵计算&#xff0c;需要针对 Pandas/Numpy 有更好的支持&#xff0c;ray库就是其中一种可以加速计算的框架。 Ray 有如下特点&#xff1a; 分布式异步调用内存调度Pandas/Numpy 的分布式支持支持 Python整体性…

springboot分布式框架搭建

搭建框架需要工具默认基于 maven 的分布式工程&#xff0c;我们知道在一个项目中&#xff0c;多个微服务是属于同一个工程&#xff0c;只不过是提供不同的服务而已&#xff0c;因此使用 maven 分布式工程来搭建微服务架构。搭建基于 maven 分布式的 Spring Cloud 微服务工程架构…

分布式事务及分布式框架Seata

分布式事务 分布式事务是什么&#xff1f; 》本地事务是一个单元的sql&#xff0c;分布式事务也是一个单元的sql&#xff0c;他们区别在于&#xff0c;分布式事务的sql分布在了不同服务上&#xff0c;这里的服务指微服务和数据库服务 &#xff1f;为什么强调服务是微服务和数…

分布式计算框架Map/reduce

简介: MapReduce是一个基于集群的高性能并行计算平台,MapReduce是一个并行计算与运行的软件框架,MapReduce是一个并行程序设计模型与方法.特点: ①分布可靠,对数据集的操作分发给集群中的多个节点实现可靠性,每个节点周期性返回它完成的任务和最新的状态 ②封装了实现细节&a…

什么是分布式架构

一、分布式架构定义 什么是分布式架构 分布式系统&#xff08;distributed system&#xff09; 是建立在网络之上的软件系统。 内聚性&#xff1a;是指每一个数据库分布节点高度自治&#xff0c;有本地的数据库管理系统。 透明性&#xff1a;是指每一个数据库分布节点对用户…

分布式架构图解

一、分布式架构图解 1&#xff09;传统servletjsp模式 2&#xff09;分布式架构  需要按照功能点把系统拆分&#xff0c;拆分成独立的功能。单独为某一个节点添加服务器。需要系统之间配合才能完成整个业务逻辑。叫做分布式。  分布式架构&#xff1a;多个子系统相互协作…

分布式框架

应用架构 单一应用架构ORM 当网站流量很小时&#xff0c;只需一个应用&#xff0c;将所有功能都部署在一起&#xff0c;以减少部署节点和成本。此时&#xff0c;用于简化增删改查工作量的数据访问框架(ORM)是关键。 适用于小型网站&#xff0c;小型管理系统&#xff0c;将所有…

juicer.js @each中的index 索引+1操作

问题描述&#xff1a;想在index1的值显示出来而不是做字符串拼接&#xff0c;如我做的楼号&#xff0c; 想显示2楼&#xff0c;3楼&#xff0c;4楼 解决方法&#xff1a;在index前加一个加号即可。 !{index1}楼 效果&#xff1a; 简单介绍juicer模板&#xff1a; Juicer 是…