目录
处理替换页面用户昵称 UserinfoServlet/doGet
处理将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客 UserinfoServlet/doGet
准备工作
在main下创建webapp文件夹下再创建WEB-INF文件夹下再创建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>
需要用到
- javaservlet3.1
- jackson 版本随意
- mysql 根据你的mysql版本
这些均在Maven Repository: Search/Browse/Explore (mvnrepository.com)网址内下载
之后复制代码粘贴到pom.xml下的 <dependecies>标签内,并且设置打包格式为war这是tomcat要求的(之后部署需要使用),再设置包名
<dependencies> <!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api --> <!--jQuery库--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <!--jackSon库--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.13.5</version> </dependency> <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j --> <!--Mysql库--> <dependency> <groupId>com.mysql</groupId> <artifactId>mysql-connector-j</artifactId> <version>8.0.33</version> </dependency> </dependencies> <!-- 打包格式--> <packaging>war</packaging> <build> <!-- 包名--> <finalName>blog_system</finalName> </build>
创建用户类博客类与连接数据库
创建博客类
博客包含
- 博客ID
- 博客标题
- 博客内容
- 创建它的作者ID
- 发布博客的时间
之后ALT+Insert生成Get和Set方法(时间的get方法有所改变,需要格式化为日期格式)
package model; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.SimpleTimeZone; //表示博客 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; } public String getPostTime() { //把时间戳构造成日期结构 SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return simpleDateFormat.format(this.postTime); } public void setPostTime(Timestamp postTime) { this.postTime = postTime; } }
创建用户类
- 用户包含
- 用户ID
- 用户名称
- 用户密码
- 判定该博客是否是登录用户的博客(后续删除博客功能需要用到、此变量不会写入数据库)
生成Get、Set方法
package model; //表示用户 public class User { private int userId; private String username; private String password; private int isYourBlog=0; public int getIsYourBlog() { return isYourBlog; } public void setIsYourBlog(int isYourBlog) { this.isYourBlog = isYourBlog; } 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; } }
创建连接数据库工具类
获取数据库连接 管理数据库的创建配置和管理
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 { //定义私有静态的DataSource对象,用于存储数据库连接信息。 //volatile确保了对这个变量的读写操作都是原子性的,即使再多线程环境下 //对这个变量访问也是线程安全的 private volatile static DataSource dataSource=null; //获取数据库连接 管理数据库的创建、配置和管理 private static javax.sql.DataSource getDataSource() { //如果为空时 加锁 if(dataSource==null){ synchronized (DBUtil.class){ if (dataSource == null) { 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("monan1946"); } } } return dataSource; } //这个方法是从DataSource获取一个具体的数据库连接 //它通常在需要执行数据库操作时调用,比如执行SQL查询或更新 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); } } } }
实现对数据库数据博客的操作
与数据库连接后,查询数据库,插入数据,删除数据操作
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; //import static jdk.nashorn.internal.objects.NativeString.substring; //Dao Data Access Object 用来访问数据的对象 //针对博客的增删改查 public class BlogDao { //插入博客 public void insert(Blog blog){ Connection connection=null; PreparedStatement statement=null; try { //与数据库建立连接 connection = DBUtil.getConnection(); //构造SQL语句 String sql = "insert into blog values(null,?,?,?,now())"; //写入sql语句 statement = connection.prepareStatement(sql); //写入问号对应的数据 statement.setString(1, blog.getTitle()); statement.setString(2, blog.getContent()); statement.setInt(3, blog.getUserId()); //执行sql int ret = statement.executeUpdate(); if (ret == 1) { System.out.println("插入成功"); } else { System.out.println("插入失败"); } }catch (SQLException e) { e.printStackTrace(); }finally { //关闭连接 DBUtil.close(connection,statement,null); } } //查询所有博客 public List<Blog> selectAll(){ Connection connection=null; PreparedStatement statement=null; ResultSet resultSet=null; List<Blog> blogs=new ArrayList<>(); try { //与数据库建立连接 connection=DBUtil.getConnection(); //构造sql //让新的先读取 页面新的在最上面 String sql="select * from blog order by postTime desc"; //写入sql statement=connection.prepareStatement(sql); //执行SQL resultSet=statement.executeQuery(); //遍历结果集合 while (resultSet.next()){ Blog blog=new Blog(); blog.setBlogId(resultSet.getInt("blogId")); blog.setTitle(resultSet.getString("title")); String content=resultSet.getString("content"); // //如果长度大于100时,只截取100个字符作为摘要 if(content.length()>100){ content=content.substring(0,100); } //如果不大于100 不截取 blog.setContent(content); blog.setUserId(resultSet.getInt("userId")); blog.setPostTime(resultSet.getTimestamp("postTime")); blogs.add(blog); } } catch (SQLException e) { e.printStackTrace(); }finally { //关闭连接 DBUtil.close(connection,statement,resultSet); } return blogs; } //查询一篇博客 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. 执行 SQL resultSet = statement.executeQuery(); // 4. 遍历结果集. 由于是按照 blogId 来查询. blogId 是自增主键, 不能重复. // 此处的查询结果不可能是多条记录. 只能是 1 条或者 0 条. 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 throwables) { throwables.printStackTrace(); } finally { // 5. 关闭资源 DBUtil.close(connection, statement, resultSet); } return null; } //删除博客 public void delete(int blogId){ Connection connection=null; PreparedStatement statement=null; ResultSet resultSet=null; try { //建立连接 connection =DBUtil.getConnection(); //构造SQL语句 String sql="delete from blog where blogId=?"; //写入sql statement=connection.prepareStatement(sql); //替换? statement.setInt(1,blogId); //执行sql int ret=statement.executeUpdate(); if(ret==1){ System.out.println("删除成功"); } else{ System.out.println("删除失败"); } } catch (SQLException e) { e.printStackTrace(); }finally { DBUtil.close(connection,statement,null); } } }
实现对数据库用户的操作
对数据库内的数据进行操作
package model; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; //Dao Data Access Object 用来访问数据的对象 //针对用户的增删改查 public class UserDao { //查询用户 public User selectByName(String username){ Connection connection=null; PreparedStatement statement=null; ResultSet resultSet=null; try { //建立连接 connection=DBUtil.getConnection(); //构造sql语句 String sql="select * from user where username=?"; //写入sql statement=connection.prepareStatement(sql); //填入问号 statement.setString(1,username); //执行sql 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) { e.printStackTrace(); }finally { DBUtil.close(connection,statement,resultSet); } return null; } public User selectById(int userId){ Connection connection=null; PreparedStatement statement=null; ResultSet resultSet=null; try { //建立连接 connection=DBUtil.getConnection(); //构造sql语句 String sql="select * from user where userId=?"; //写入sql statement=connection.prepareStatement(sql); //填入问号 statement.setInt(1,userId); //执行sql 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) { e.printStackTrace(); }finally { DBUtil.close(connection,statement,resultSet); } return null; } }
创建数据库语句
需要把创建数据库的语句放入db.sql中,方便后续更换数据库使用
create database blog_system; use blog_system; create table blog( blogId int primary key auto_increment, --主键 自增 博客ID title varchar(1024), --博客标题 content mediumtext, --博客正文 mediutext类型很长 userId int, --作者id postTime datetime --发布事件 ); create table blog( blogId int primary key auto_increment, title varchar(1024), content mediumtext, userId int, postTime datetime ); create table user( userId int primary key auto_increment, --用户id username varchar(128) unique , --用户名 password varchar(128) --密码 ); create table user( userId int primary key auto_increment, username varchar(128) unique , password varchar(128) );
登录页面
前端
login/loginServlet
前端将用户写入的数据已post方法提交给后端
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <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/login.css"> </head> <body> <!-- 导航栏 --> <div class="nav"> <!-- logo图片 --> <img src="image/logo.jpg" alt="" width="50px"> <span class="title">我的博客系统</span> <span class="spacer"></span> <!-- 导航栏 --> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> </div> <!-- 登录板块 --> <div class="login-container"> <div class="login-dialog"> <p>登录</p> <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" value="提交"id="submit"> </div> </form> </div> </div> </body> </html>
后端
- 后端调用doPost方法
- 使用resp.getParameter方法获取数据
- 去数据库内查询数据。
- 查询不到则响应登录失败
- 若查询到时,创建session HttpSession session= req.getSession()
- 也就是浏览器内的cookie session.setAttribute把此用户存储进去,方便后续使用之后登录成功跳转至博客列表
package controller; 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 { @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //告诉servlet(服务器)按照utf格式解析请求中的body //如果不使用这个 中文格式的会乱码 req.setCharacterEncoding("utf8"); //告诉浏览器 按照utf8格式解析响应 // resp.setCharacterEncoding("utf8"); resp.setContentType("text/html;charset=utf8"); String username=req.getParameter("username"); String password=req.getParameter("password"); // System.out.println(username); // System.out.println(password); // if(username.equals("user")&&password.equals("123")){ // resp.getWriter().write("登录成功"); // } // else // { // resp.getWriter().write("用户名或密码错误"); // } UserDao userDao=new UserDao(); User user=userDao.selectByName(username); if(user==null){ resp.getWriter().write("用户名或密码错误"); return; } if(!user.getPassword().equals(password)){ resp.getWriter().write("用户名或密码错误"); return; } //到这里就是登录成功了 //构造会话 cookie HttpSession session= req.getSession(); //把信息存到cookie内,方便后续使用 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){ //如果连session都没有 返回403状态码 //403状态码就是代表用户未登录的状态码 resp.setStatus(403); return; } //取cookie查找是否有user这个用户 //这个user不是用户名 而是user内的用户名 User user= (User) session.getAttribute("user"); if(user==null){ //没有这个用户,说明未登录 resp.setStatus(403); return; } //到这说明用户已经登录成功 resp.setStatus(200); } }
博客列表
前端
前端使用ajax方法,所以需要引入jquery
jquery需要从jQuery CDN 链接内点击minified 复制
https://code.jquery.com/jquery-3.7.1.min.js
之后引入即可使用
前端整体代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <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.css"> </head> <body> <!-- 导航栏 --> <div class="nav"> <!-- logo图片 --> <img src="image/logo.jpg" alt="" width="50px"> <span class="title">我的博客系统</span> <span class="spacer"></span> <!-- 导航栏 --> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <a href="logout">注销</a> <!-- a会直接向对应路径发起get请求--> </div> <!-- 页面主体 --> <div class="container"> <!-- 左侧个人信息 --> <div class="container-left"> <div class="card"> <img src="image/蕾姆.jpg" alt=""> <h2>墨轩111</h2> <a href="#">github链接</a> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>3</span> </div> </div> <!-- 右侧个人信息 --> </div> <div class="container-right"> <!-- <div class="blog">这是我的第一篇文章</div> <div class="date">2024-3-27 20:20</div> <div class="desc">文章内容Lorem ipsum dolor sit amet consectetur, adipisicing elit. At laboriosam quis quos, numquam tenetur cum ipsa architecto nihil, a asperiores error fugit. Praesentium eaque animi esse nemo ut nobis atque.</div> <a href="blog_detail.html">查看全文 >> </a> --> <!-- >是html的> 正常的>无法之间写入 --> </div> </div> <!-- 引入jquery --> <script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="js/app.js"></script> <script> function getBlogs(){ //构造ajax $.ajax({ type:'get', url:'blog', success: function(body){ let container=document.querySelector('.container-right'); //后端返回的是json数据 //如果Content-Type是application/json,jquery ajax会把body转成js对象 for(let blog of body){ //构造最外层的div let blogDiv=document.createElement('div'); blogDiv.className='blog'; //创建博客标题 let titleDiv=document.createElement('div'); titleDiv.className='title'; //把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值 titleDiv.innerHTML=blog.title; //把titleDiv添加在blogDiv内 blogDiv.appendChild(titleDiv); //创建日期 let dateDiv=document.createElement('div'); dateDiv.className='date'; //把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值 dateDiv.innerHTML=blog.postTime; //把dateDiv添加到blogDiv内 blogDiv.appendChild(dateDiv); //创建内容 let descDiv=document.createElement('div'); descDiv.className='desc'; //把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值 descDiv.innerHTML=blog.content; //把descDiv添加到blogDiv内 blogDiv.appendChild(descDiv); //创建按钮 let a=document.createElement('a'); a.innerHTML='查看全文 >>'; //传递这篇博客的ID是多少 后端才能取查询这篇博客的详细页 a.href='blog_detail.html?blogId='+blog.blogId; blogDiv.appendChild(a); //把blogDiv添加到container内 container.appendChild(blogDiv); } } }); } getBlogs(); //使用ajax构造get方法。判断当前是否用户 // 如果没登陆,强制跳转到登录页面 checkLogin(); //将博客列表的昵称替换为当前登录用户的昵称 function GetUser(){ $.ajax({ type: 'get', url:'userInfo', //后端查询失败时,发送状态码为403 403不会触发回调函数 success:function (body){ //将登录的用户名替换上去 let user=document.querySelector('.card h2'); user.innerHTML=body.username; } }); } GetUser(); </script> </body> </html>
注销登录
注意:注销登录和注销账号是不同的,注销登录即退出登录,而注销账户是删除账号
<a href="logout">注销</a> <!-- a会直接向对应路径发起get请求-->
写入数据
- 前端构造ajax构造get方法,发送给后端。
- 调用回调函数。
- 遍历后端传来的数据(想要用遍历,那么后端传来的数据一定是像List这种可遍历的,如何只传来的对象,那就无法遍历)
- 创建div 分别把博客的标题,时间,内容创建,把后端数据都写入。并且创建查询博客详情页的按键。当按下这个按键时,会把博客ID传递给博客详情页,详情页就能拿着这个ID去后端查询此博客
- 把整体div放入原先的div内
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script> <script src="js/app.js"></script> <script> function getBlogs(){ //构造ajax $.ajax({ type:'get', url:'blog', success: function(body){ let container=document.querySelector('.container-right'); //后端返回的是json数据 //如果Content-Type是application/json,jquery ajax会把body转成js对象 for(let blog of body){ //构造最外层的div let blogDiv=document.createElement('div'); blogDiv.className='blog'; //创建博客标题 let titleDiv=document.createElement('div'); titleDiv.className='title'; //把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值 titleDiv.innerHTML=blog.title; //把titleDiv添加在blogDiv内 blogDiv.appendChild(titleDiv); //创建日期 let dateDiv=document.createElement('div'); dateDiv.className='date'; //把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值 dateDiv.innerHTML=blog.postTime; //把dateDiv添加到blogDiv内 blogDiv.appendChild(dateDiv); //创建内容 let descDiv=document.createElement('div'); descDiv.className='desc'; //把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值 descDiv.innerHTML=blog.content; //把descDiv添加到blogDiv内 blogDiv.appendChild(descDiv); //创建按钮 let a=document.createElement('a'); a.innerHTML='查看全文 >>'; //传递这篇博客的ID是多少 后端才能取查询这篇博客的详细页 a.href='blog_detail.html?blogId='+blog.blogId; blogDiv.appendChild(a); //把blogDiv添加到container内 container.appendChild(blogDiv); } } }); } getBlogs();
判断用户是否登录
因为这个功能是多个页面都会使用,所以写入js文件内,在前面引入js即可调用这个方法
//使用ajax构造get方法。判断当前是否用户 // 如果没登陆,强制跳转到登录页面 checkLogin(); 这个在js内的app.js文件 function checkLogin(){ $.ajax({ type: 'get', url:'login', success:function (){ // 当客户端响应200状态码时,说明用户已登录,不做处理 }, error:function (){ //发客户端响应403时 就会触发error //强制跳转登录页面 location.assign('login.html'); } }) }
替换页面用户昵称
向后端发起get方法,把页面昵称替换为当前登录用户的昵称
function GetUser(){ $.ajax({ type: 'get', url:'userInfo', //后端查询失败时,发送状态码为403 403不会触发回调函数 success:function (body){ //将登录的用户名替换上去 let user=document.querySelector('.card h2'); user.innerHTML=body.username; } }); } GetUser();
后端
处理注销登录 LogoutServlet/doGet
- req.getSession查询是否创建了session
- 使用session.removeAttribute删除session的当前的用户
- resp.sendRedirect强制跳转到登录页面
@WebServlet("/logout") public class LogoutServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //注销功能 //删除session中的对象 就相当于取消状态 //查找是否有session HttpSession session=req.getSession(false); if(session==null) { resp.setStatus(403); return; } //删除session中的user session.removeAttribute("user"); //重定向到登录页面 resp.sendRedirect("login.html"); } }
处理获取所有博客 BlogServlet/doGet
- 想要构造JSON格式,就得创建ObjectMapper
- 因为前端使用的是ajax,后端需要响应json格式。所以需要用resp.setContentType设置响应格式 (application/json;charset=utf8)
- 创建操作博客内容的数据库类
- 查看req.getParameter获取前端是否传来了博客ID,如果传来了,说明前端想要查询单个博客(这里是博客详情页,后续讲)
- 如果没传博客ID,说明前端想要查询所有博客,博客列表是查询所有博客
- 调用blogDo查询博客类,查询所有博客,并且把这些数据放入List,保证所有博客可以遍历。
- 使用objectMapper.writeValueAsString将数据构造成json格式
- 响应数据
@Override //发送博客列表或博客详情页的内容 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //想要构造JSON格式,就得用到此类 private ObjectMapper objectMapper=new ObjectMapper(); resp.setContentType("application/json;charset=utf8"); //要拿到写数据库操作的类 BlogDao blogDao=new BlogDao(); //获取url中的博客ID String blogId=req.getParameter("blogId"); System.out.println(blogId); //为空时 说明时获取博客列表,而不是获取博客详情 if(blogId==null){ //查询所有 返回的是list 每个类型是一个博客类型 List<Blog> blogs=blogDao.selectAll(); //转成json格式 String jsonString=objectMapper.writeValueAsString(blogs); //响应 resp.getWriter().write(jsonString); }else{ //如果获取到博客ID时,说明时获取博客详情页 //使用数据库查找这个博客ID Blog blog=blogDao.selectOne(Integer.parseInt(blogId)); //记住,传json格式,如果你要用for循环 那么你的对象一定要是可迭代的对象 //如果你单构造个类过去,你用for是肯定循环不了的,你必须使用List这种可以迭代的对象,才能使用这个迭代循环 List<Blog> blogs=new ArrayList<>(); blogs.add(blog); //你也可以单传个对象 到前端那不用循环直接使用也可以 // List<Blog> blogs=new ArrayList<>(); // String jsonString=objectMapper.writeValueAsString(blog); //转为json格式 String jsonString=objectMapper.writeValueAsString(blogs); //响应 resp.getWriter().write(jsonString); } }
处理判断用户是否登录 loginServlet/doGet
- req.getSession查询是否创建了session(这里可以直接判断用户是否登录了,因为只要登录,那么就一定创建了session
- 为空时,resp.setStatus响应状态码403
- 如果不为空时,session.getAttribute查询session是否有该用户
- 如果为空,则有创建session,但没有此用户,setStatus响应状态码403
- 如果也不为空,说明该用户以及登录,setStatus响应状态码200
- 响应状态码403是为了不让前端调用回调函数,当返回200时,才调用
@Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //验证登录状态 //如果用户未登录,强制重定向到登录页面 //如果用户已登录,不做动作 //取查询session HttpSession session=req.getSession(false); if(session==null){ //如果连session都没有 返回403状态码 //403状态码就是代表用户未登录的状态码 resp.setStatus(403); return; } //取cookie查找是否有user这个用户 //这个user不是用户名 而是user内的用户名 User user= (User) session.getAttribute("user"); if(user==null){ //没有这个用户,说明未登录 resp.setStatus(403); return; } //到这说明用户已经登录成功 resp.setStatus(200); }
处理替换页面用户昵称 UserinfoServlet/doGet
- 想要构造JSON格式,就得创建此类objectMapper
- req.getParameter获取blogId(如果为空,则是博客列表发来的,想要获取此博客的用户名,如果不为空,是博客详情页发来的,判断此博客是否是当前登录用户的博客)
- req.getSession判断是否有session
- session.getAttribute 查询session是否有该用户,获取此用户
- 当blogId为空时,博客列表发来的
- 首先用户的密码设为空串 (不是修改数据库内的数据)
- resp.setContentType设置响应格式
- objectMapper.writeValueAsString数据构造为JSON格式
- 响应数据
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //想要构造json 就要创建这个类 ObjectMapper objectMapper=new ObjectMapper(); String blogid=req.getParameter("blogId"); //直接取查找是否有cookie HttpSession session=req.getSession(false); if(session==null){ //后端查询失败时,发送状态码为403 403不会触发回调函数 resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } //获取当前登录用户 User user= (User) session.getAttribute("user"); if(user==null){ resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } if(blogid==null){ //查询登录的用户 //当url中的查询字符串中没有blogId时,说明是博客列表发来的请求 //不用让密码在body中明文显示 user.setPassword(""); resp.setContentType("application/json;charset=utf8"); //构造成json格式 //返回登录的用户 String jsonString=objectMapper.writeValueAsString(user); //写入 resp.getWriter().write(jsonString);
博客详情页
前端
总体
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <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.css"> <link rel="stylesheet" href="css/blog_detail.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.js"></script> </head> <body> <!-- 导航栏 --> <div class="nav"> <!-- logo图片 --> <img src="image/logo.jpg" alt="" width="50px"> <span class="title">我的博客系统</span> <span class="spacer"></span> <!-- 导航栏 --> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <a href="logout">注销</a><!-- a会直接向对应路径发起get请求--> <div class="dete"></div> <!-- 删除博客--> </div> <!-- 页面主体 --> <div class="container"> <!-- 左侧个人信息 --> <div class="container-left"> <div class="card"> <img src="image/蕾姆.jpg" alt=""> <h2>墨轩111</h2> <a href="#">github链接</a> <div class="counter"> <span>文章</span> <span>分类</span> </div> <div class="counter"> <span>2</span> <span>3</span> </div> </div> </div> <!-- 右侧个人信息 --> <!-- <div class="container-right">--> <!-- </div>--> <div class="container-right"> <!-- 这个 div 里来放博客的内容 --> <div class="blog-content"> </div> </div> </div> <script src="https://code.jquery.com/jquery-3.6.1.min.js"></script> <script src="js/app.js"></script> <script> function getBlogs() { //构造ajax $.ajax({ type: 'get', //博客列表传来的 url: 'blog' + location.search, success: function (body) { let container = document.querySelector('.container-right'); //后端返回的是json数据 //如果Content-Type是application/json,jquery ajax会把body转成js对象 for (let blog of body) { //构造最外层的div let blogDiv = document.createElement('div'); blogDiv.className = 'blog'; //创建博客标题 let titleDiv = document.createElement('div'); titleDiv.className = 'title'; //把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值 titleDiv.innerHTML = blog.title; //把titleDiv添加在blogDiv内 blogDiv.appendChild(titleDiv); //创建日期 let dateDiv = document.createElement('div'); dateDiv.className = 'date'; //把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值 dateDiv.innerHTML = blog.postTime; //把dateDiv添加到blogDiv内 blogDiv.appendChild(dateDiv); //创建内容 let descDiv = document.createElement('div'); descDiv.className = 'desc'; //把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值 descDiv.innerHTML = blog.content; //把descDiv添加到blogDiv内 blogDiv.appendChild(descDiv); //把blogDiv添加到container内 container.appendChild(blogDiv); } } }); } getBlogs(); checkLogin(); function GetUser(){ $.ajax({ type: 'get', url:'userInfo'+location.search,//加个博客ID 让后端查找这篇博客 success:function (body){ //将这篇作者的用户名替换上去 let user=document.querySelector('.card h2'); user.innerHTML=body.username; //当这篇博客是当前用户的 那么就显示删除按钮 if(body.isYourBlog){ let delet=document.createElement('a'); delet.className="delete"; delet.innerHTML="删除博客" delet.href='blogdelete'+location.search; let de=document.querySelector('.dete'); de.appendChild(delet); } } }); } GetUser(); </script> </body> </html>
注销登录
与博客列表一致这里不再赘述
查询博客
- 构造ajax的get方法,url是博客列表传来的博客ID,传给后端,后端才能根据这个ID查询博客
- 后端传来了数据,只要是json格式(application/json;charset=utf8)那么ajax就会把body转成js的对象。
- 创建div,分别把博客的标题、时间、内容写入
<script src="https://code.jquery.com/jquery-3.6.1.min.js"></script> <script src="js/app.js"></script> <script> function getBlogs() { //构造ajax $.ajax({ type: 'get', //博客列表传来的 url: 'blog' + location.search, success: function (body) { let container = document.querySelector('.container-right'); //后端返回的是json数据 //如果Content-Type是application/json,jquery ajax会把body转成js对象 for (let blog of body) { //构造最外层的div let blogDiv = document.createElement('div'); blogDiv.className = 'blog'; //创建博客标题 let titleDiv = document.createElement('div'); titleDiv.className = 'title'; //把后端传来的json,被jackson解析成blog对象,对象内的标题属性赋值 titleDiv.innerHTML = blog.title; //把titleDiv添加在blogDiv内 blogDiv.appendChild(titleDiv); //创建日期 let dateDiv = document.createElement('div'); dateDiv.className = 'date'; //把后端传来的json,被jackson解析成blog对象 对象内的时间属性赋值 dateDiv.innerHTML = blog.postTime; //把dateDiv添加到blogDiv内 blogDiv.appendChild(dateDiv); //创建内容 let descDiv = document.createElement('div'); descDiv.className = 'desc'; //把把后端传来的json,被jackson解析成blog对象 对象内的内容属性赋值 descDiv.innerHTML = blog.content; //把descDiv添加到blogDiv内 blogDiv.appendChild(descDiv); //把blogDiv添加到container内 container.appendChild(blogDiv); } } }); } getBlogs();
判断用户是否登录
这个与博客列表页一样,这里不再赘述
将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客
- 构造ajax的get方法,url传入博客列表传来的博客ID
- 获取数据后,将后端的数据写入昵称
- 判断isYourBlog是否为1 (1是说明当前登录用户的ID与博客ID相同(此篇博客是当前用户写的),说明当前用户有支配此篇博客的权利,显示删除博客 0说明当前登录用户的ID与博客ID不同(此篇博客不是当前用户写的,没有支配全,不显示删除博客)
- 为1时,创建a标签,命名为删除博客,链接到删除博客的后端,并且传入博客ID
- 再放入原先的div
function GetUser(){ $.ajax({ type: 'get', url:'userInfo'+location.search,//加个博客ID 让后端查找这篇博客 success:function (body){ //将这篇作者的用户名替换上去 let user=document.querySelector('.card h2'); user.innerHTML=body.username; //当这篇博客是当前用户的 那么就显示删除按钮 if(body.isYourBlog){ let delet=document.createElement('a'); delet.className="delete"; delet.innerHTML="删除博客" delet.href='blogdelete'+location.search; let de=document.querySelector('.dete'); de.appendChild(delet); } } }); } GetUser();
后端
处理注销登录 LogoutServlet/doGet
这里与博客列表一致,不再赘述
处理查询博客 BlogServlet/doGet
- resp.setContentTyoe设置响应格式 (application/json;charset=utf8)
- 创建操作博客类
- req.getParameter获取前端传来的博客ID
- 这里是查询单个博客,所以是不为空的
- 将博客ID转为int 并且从数据库内查询
- 将博客放入List(这里最好不用放入List,因为只是单个博客,不需要迭代,我只是演示)
- objectMapper.writeVlueAsString构造成JSON格式
- 响应数据
else{ resp.setContentType("application/json;charset=utf8"); //如果获取到博客ID时,说明时获取博客详情页 //使用数据库查找这个博客ID String blogId=req.getParameter("blogId"); Blog blog=blogDao.selectOne(Integer.parseInt(blogId)); //记住,传json格式,如果你要用for循环 那么你的对象一定要是可迭代的对象 //如果你单构造个类过去,你用for是肯定循环不了的,你必须使用List这种可以迭代的对象,才能使用这个迭代循环 List<Blog> blogs=new ArrayList<>(); blogs.add(blog); //你也可以单传个对象 到前端那不用循环直接使用也可以 // List<Blog> blogs=new ArrayList<>(); // String jsonString=objectMapper.writeValueAsString(blog); //转为json格式 String jsonString=objectMapper.writeValueAsString(blogs); //响应 resp.getWriter().write(jsonString); }
处理判断用户是否登录 LoginServlet/doGet
这里是与博客列表是一样的,就不再赘述
处理将博客详情页昵称替换为当前博客作者昵称/是否显示删除博客 UserinfoServlet/doGet
- 创建要构造JSON的类
- resp.getParameter获取blogId
- req.GetSession查询是否有session
- session.getAttribute获取session是否有该用户
- 数据库中查询此篇博客
- 数据中查询用户类是否有此篇博客的作者
- 当都有时,判断当前session的用户ID与此篇博客的ID是否相同(相同时,用户类的isyourblog设置1反之设置0)
- 响应时,把用户密码设置为0不要名为显示,不修改数据库的数据
- resp.setContentType设置响应格式
- objectMapper.writeValueAsString构造为JSON格式
- 响应
//想要构造json 就要创建这个类 ObjectMapper objectMapper=new ObjectMapper(); String blogid=req.getParameter("blogId"); //直接取查找是否有cookie HttpSession session=req.getSession(false); if(session==null){ //后端查询失败时,发送状态码为403 403不会触发回调函数 resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } //获取当前登录用户 User user= (User) session.getAttribute("user"); if(user==null){ resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } else{ //查询这篇博客的作者 //如果有blogId时,说明是博客详情页发来的请求 BlogDao blogDao=new BlogDao(); Blog blog=blogDao.selectOne(Integer.parseInt(blogid)); if(blog==null){ resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } UserDao userDao=new UserDao(); //用博客类里的用户id 去用户类去查询 查询用户是否有这个博客作者 User author=userDao.selectById(blog.getUserId()); if(author==null){ resp.setStatus(403); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("没有该用户"); return; } if(author.getUserId()== user.getUserId()){ author.setIsYourBlog(1); }else { author.setIsYourBlog(0); } //不用让密码在body中明文显示 user.setPassword(""); resp.setContentType("application/json;charset=utf8"); String jsonString=objectMapper.writeValueAsString(author); //返回这篇博客的作者 resp.getWriter().write(jsonString); }
博客编辑页
前端
通过from表单提交标题,内容
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <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-container.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.js"></script> </head> <body> <!-- 导航栏 --> <div class="nav"> <!-- logo图片 --> <img src="image/logo.jpg" alt="" width="50px"> <span class="title">我的博客系统</span> <span class="spacer"></span> <!-- 导航栏 --> <a href="blog_list.html">主页</a> <a href="blog_edit.html">写博客</a> <a href="logout">注销</a><!-- a会直接向对应路径发起get请求--> </div> <div class="blog-edit-container"> <form action="blog" method="post" style="height: 100%"> <!-- 标题编辑区 --> <div class="title"> <input type="text" id="title" placeholder="请输入文章标题" name="title"> <input type="submit" id="submit" value="发布文章"> </div> <!-- 博客编辑器标签 --> <div id="editor"> <!-- 需要在这里加上一个隐藏的 textarea --> <!-- 属于 editor.md 这个库要求的. --> <textarea name="content" style="display: none;" ></textarea> </div> </form> </div> <script> // 初始化编辑器 var editor = editormd("editor", { // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. width: "100%", // 设定编辑器高度 height: "calc(100% - 50px)", // 编辑器中的初始内容 markdown: "# 在这里写下一篇博客", // 指定 editor.md 依赖的插件路径 path: "editor.md/lib/", // 加上这个属性, 效果就是把编辑器里的内容给自动保存到 textarea 里. saveHTMLToTextArea: true, }); </script> </body> </html>
后端 BlogServlet/doPost
- req.setCharacterEncoding设置后端读取的格式
- req.getSession判断session是否存在
- session.getAttribute判断用户是否在session内
- 使用req.getParameter获取标题、内容
- 如果标题内容为空或者为空串,则响应400状态码
- 创建一个博客类
- 将数据写入博客类中
- 博客的作者ID就是当前登录用户的ID
- 最后将这个类写入数据库(实际就是将类中的数据写入)
- 写入后,resp.sendRedirect跳转到博客列表
//提交博客 @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("utf8"); //判断会话是否存在和会话中的用户是否存在 HttpSession session=req.getSession(false); if (session == null) { resp.setStatus(400); return; } User user= (User) session.getAttribute("user"); // System.out.println(user.getUsername()); // System.out.println(user.getUserId()); if(user==null){ resp.setStatus(400); return; } //2.获取博客的标题和正文 String title=req.getParameter("title"); String content=req.getParameter("content"); // System.out.println(title); // System.out.println(content); if(title==null||content==null||title.equals("")||content.equals("")){ resp.setStatus(400); resp.setContentType("text/html;charset=utf8"); resp.getWriter().write("标题或正文不符合"); return; } //把数据写入一个对象 Blog blog=new Blog(); blog.setTitle(title); blog.setContent(content); //博客作者的ID就是当前登录用户的ID 所以直接从session拿出当前用户 再拿出当前用户的id blog.setUserId(user.getUserId()); //创建博客写入数据库的对象 BlogDao blogDao=new BlogDao(); //把这个博客对象写入数据库 blogDao.insert(blog); //响应 resp.sendRedirect("blog_list.html"); }
部署
准备工作
均在Linux系统下
下载jdk
yum install java-1.8.0-openjdk-devel.x86_64
下载tomcat
把window下的tomcat8.0拖拽到linux上
解压
unzip apache-tomcat-8.5.100.zip
加权限
cd到tomcat的bin目录下
给所有.sh加上权限
chmod +x *.sh
./即可启动
startup.sh是linux启动脚本 startup.bat是window启动脚本
但是大概率启动不了,因为tomcat8080端口,服务器是默认不开放的,所有要进入你的服务器控制台开放端口
安装mysql
之后把db.sql里的sql语句复制到数据库内,重新建库建表
使用maven打包
记得改包格式和包名
把打好的包从window拖拽到Linux的Tomcat的webappsxia
访问
你服务器的地址:端口:包名:你也访问的页面