当前位置: 首页 > news >正文

【JavaEE初阶】博客系统后端

文章目录

  • 一. 创建项目 引入依赖
  • 二. 设计数据库
  • 三. 编写数据库代码
  • 四. 创建实体类
  • 五. 封装数据库的增删查改
  • 六. 具体功能书写
    • 1. 博客列表页
    • 2. 博客详情页
    • 3. 博客登录页
    • 4. 检测登录状态
    • 5. 实现显示用户信息的功能
    • 6. 退出登录状态
    • 7. 发布博客

一. 创建项目 引入依赖

创建blog_system项目.将之前写的博客系统前端代码复制到webapp目录下.
在这里插入图片描述
pom.xml中引入Servlet mysql jackson三个依赖:
pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>org.example</groupId><artifactId>blog_system</artifactId><version>1.0-SNAPSHOT</version><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target></properties><dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.49</version></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.14.2</version></dependency></dependencies>
</project>

二. 设计数据库

结合之前的需求,在当前博客系统中,主要涉及到两个实体.即 用户博客.
经过分析我们可以得到,用户博客之间是一对多的关系.即一个用户可以拥有多篇博客.
所以我们可以创建两张表,来表示用户和博客.
blog(blogId,title,content,postTime,userId)
user(userId,userName,password)

三. 编写数据库代码

把一些基本的数据库操作,先封装好.
在这里插入图片描述
需要手动粘贴至MySQL

-- 这个文件主要用来写 建库建表 语句create database if not exists javaee_blog_system;
use javaee_blog_system;-- 删除旧表,重新建表
drop table if exists user;
drop table if exists blog;-- 创建表
create table blog(blogId int primary key auto_increment,title varchar(128),content varchar(4096),postTime datetime,userId int
);create table user(userId int primary key auto_increment,username varchar(20) unique,-- 要求用户名和别人的不重复password varchar(20)
);

接下来是封装数据库的连接操作:
DBUtil:

import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;import javax.sql.DataSource;
import java.sql.*;public class DBUtil {private static DataSource dataSource = new MysqlDataSource();static {((MysqlDataSource)dataSource).setUrl("jdbc:mysql://127.0.0.1:3306/javaee_blog_system?characterEncoding=utf8&useSSL=false");((MysqlDataSource)dataSource).setUser("root");((MysqlDataSource)dataSource).setPassword("0828");}public static Connection getConnection() throws SQLException {return dataSource.getConnection();}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();}}}
}

四. 创建实体类

实体类就是和表中的记录对应的类:
blog表 =>Blog类对应Blog的一个对象,就对应表中的一条记录.
user表 =>User类对应User的一个对象,就对应表中的一个记录.
实体类有哪些属性,都是和当前表中的列是密切相关的.
User:


public class User {private int userId;private String username;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;}private String password;
}

Blog:

import java.sql.Timestamp;public class Blog {private int blogId;private String title;private String content;private Timestamp postTime;private int userId;public int getBlogId() {return blogId;}public void setBlogId(int blogId) {this.blogId = blogId;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}public Timestamp getPostTime() {return postTime;}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}
}

五. 封装数据库的增删查改

针对博客表,创建BlogDao;(Data Access Object)
针对用户表,创建UserDao.
上述Dao用来提供一些方法.来进行增删查改.
BlogDao:

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 add(Blog blog){Connection connection = null;PreparedStatement statement = null;try {//1.和数据库建立连接connection = DBUtil.getConnection();//2.构造SQLString sql = "insert into blog values(null,?,?,?,?)";statement = connection.prepareStatement(sql);statement.setString(1,blog.getTitle());statement.setString(2, blog.getContent());statement.setTimestamp(3,blog.getPostTime());statement.setInt(4,blog.getUserId());//3.执行SQLstatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);}finally {DBUtil.close(connection,statement,null);}}//2.根据博客id来查询指定博客(博客详情页)public Blog selectById(int blogId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sqlString sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);//3.执行sqlresultSet = statement.executeQuery();//4.遍历结果集合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"));}} catch (SQLException e) {throw new RuntimeException(e);}finally {//5.释放资源DBUtil.close(connection,statement,resultSet);}return null;}//3.查询数据库中所有的博客列表(用于博客列表页)public List<Blog> selectAll(){List<Blog> blogs = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "select * from blog";statement = connection.prepareStatement(sql);//3.执行sqlresultSet = statement.executeQuery();//4.遍历结果集合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() >= 100){content = content.substring(0,100) +"...";}blog.setContent(content);blog.setPostTime(resultSet.getTimestamp("postTime"));blog.setUserId(resultSet.getInt("userId"));blogs.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);}finally {DBUtil.close(connection,statement,resultSet);}return blogs;}//4.删除指定博客public void delect(int blogId){Connection connection = null;PreparedStatement statement = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sql语句String sql = "delete from blog where bolgId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,blogId);//3.执行sqlstatement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);}finally {//4.关闭资源DBUtil.close(connection,statement,null);}}
}

UserDao:

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;//针对用户表提供的基本操作
public class UserDao {//1.根据 userId 来查用户信息public User selectById(int userId){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sqlString sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,userId);//3.执行SQLresultSet = 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) {throw new RuntimeException(e);}finally {//5.释放资源DBUtil.close(connection,statement,resultSet);}return null;}//2.根据username来查找用户信息.public User selectByUsername(int username){Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {//1.与数据库建立连接connection = DBUtil.getConnection();//2.构造sqlString sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1,username);//3.执行SQLresultSet = 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) {throw new RuntimeException(e);}finally {//5.释放资源DBUtil.close(connection,statement,resultSet);}return null;}
}

六. 具体功能书写

1. 博客列表页

在这里插入图片描述
当前博客列表页上的数据都是写死的.正确的做法,应该是通过数据库读取数据显示到页面上.
此处就需要打通前后端交互的操作.
让博客列表页,在加载的时候,通过ajax给服务器发一个请求服务器查数据库获取到博客列表数据,返回给浏览器,浏览器再根据数据构造页面内容.这样的交互过程,也称为“前后端分离"
前端只向后端请求数据,而不请求具体的页面,后端也仅仅是返回数据.这样的设定的目的就是为了前端和后端更加解耦,由浏览器进行具体的页面渲染.减少了服务器的工作量.

上述进行实现博客列表页的基本思路

接下来需要:

  1. 约定前后端交互接口
  2. 开发后端代码
  3. 开发前端代码
  1. 约定前后端交互接口
    请求:GET /blog
    响应:使用json格式的数据来组织
[{blogId:1,title:"这是第一篇博客",content:"从今天开始,我要认真学习",postTime:"2023-08-01 09:40:45",userId:1}{blogId:1,title:"这是第一篇博客",content:"从今天开始,我要认真学习",postTime:"2023-08-01 09:40:45",userId:1}{blogId:1,title:"这是第一篇博客",content:"从今天开始,我要认真学习",postTime:"2023-08-01 09:40:45",userId:1}
]
  1. 开发后端代码
    BlogServlet:
package api;import com.fasterxml.jackson.databind.ObjectMapper;
import model.Blog;
import model.BlogDao;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.List;public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {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);}
}

在这里插入图片描述
此处注意文件所在位置.model是管理数据/操作数据的部分.

  1. 开发前端代码
    在博客列表页加载过程中,触发ajax,访问服务器中的数据.再把拿到的数据构造到页面中.
<!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_list.css">
</head>
<body><!-- 导航栏 --><div class="nav"><img src="image/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用于占位,将a标签挤到右侧去 --><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a><a href="#">注销</a></div><!-- 页面主体部分 --><div class="container"><!-- 左侧信息 --><div class="container-left"><!-- 使用.card表示用户信息 --><div class="card"><!-- 用户头像 --><img src="image/touxiang.jpg" alt=""><!-- 用户名 --><h3>xxxflower</h3><a href="#">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></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script><script>// 在页面加载时, 向服务器发起请求, 获取博客列表数据function getBlogs() {$.ajax({type: 'get',url: 'blog',success: function(body) {// 响应的正文 是一个 json 字符串, 此处已经被 jquery 自动解析成 js 对象数组了. // 直接 for 循环遍历即可. let containerRight = document.querySelector('.container-right');for (let blog of body) {// 构造页面内容, 参考之前写好的 html 代码// 构造整个博客 divlet 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;';// 期望点击之后能跳转到博客详情页. 为了让博客详情页知道是点了哪个博客, 把 blogId 给传过去a.href = 'blog_detail.html?blogId=' + blog.blogId;blogDiv.appendChild(a);// 把 blogDiv 加到父元素中containerRight.appendChild(blogDiv);}}});}// 要记得调用getBlogs();</script>
</body>
</html>

效果如下图所示:
在这里插入图片描述
但是我们发现这里的时间显示有问题:
我们需要使用SimpleDateFormat类.注意该类使用时的书写格式!
在这里插入图片描述
在这里插入图片描述

2. 博客详情页

关于博客详情页,点击查看全文按钮,就能跳转到博客详情页中.跳转过去之后,在博客详情页中发起一个ajax,从服务器获取到当前的博客的具体内容.再进行显示.

分为以下几个步骤:

  1. 约定前后端交互接口
  2. 实现后端代码
  3. 实现前端代码
  1. 约定前后端交互接口
    请求:GET/blog?blogId = 1
    响应:
    HTTP/1.1 200 OK
    {
    blogId:1,
    title:“这是第一篇博客”,
    content:“从今天开始我要认真学习”,
    postTime:“2023-08-01 17:00:00”,
    userId:1
    }

  2. 实现后端代码

<!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_detail.css">
</head>
<body><!-- 导航栏 --><div class="nav"><img src="image/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用于占位,将a标签挤到右侧去 --><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a><a href="#">注销</a></div><!-- 页面主体部分 --><div class="container"><!-- 左侧信息 --><div class="container-left"><!-- 使用.card表示用户信息 --><div class="card"><!-- 用户头像 --><img src="image/touxiang.jpg" alt=""><!-- 用户名 --><h3>xxxflower</h3><a href="#">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 class="title"></h3><!-- 博客发布时间 --><div class="date"></div><!-- 博客正文, 为了配合 editormd 进行格式转换, 此处务必改成 id  --><div id="content"></div></div></div><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.3/jquery.min.js"></script><!-- 要保证这几个 js 的加载在 jquery 之后. editor.md 依赖了 jquery --><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>$.ajax({type: 'get',url: 'blog' + location.search,success: function(body) {// 处理响应结果, 此处的 body 就是表示一个博客的 js 对象. // 1. 更新标题let titleDiv = document.querySelector('.container-right .title');titleDiv.innerHTML = body.title;// 2. 更新日期let dateDiv = document.querySelector('.date');dateDiv.innerHTML = body.postTime;// 3. 更新博客正文// 此处不应该直接把博客正文填充到这个标签里editormd.markdownToHTML('content', { markdown: body.content });}})</script>
</body>
</html>
  1. 开发后端代码
package api;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;@WebServlet("/blog")
public class BlogServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {BlogDao blogDao = new BlogDao();String blogId = req.getParameter("blogId");if(blogId == null){List<Blog> blogs = blogDao.selectAll();//需要把blogs转成符合要求的 json 格式字符串String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset = utf8");resp.getWriter().write(respJson);}else{// queryString 存在, 说明本次请求获取的是指定 id 的博客.Blog blog = blogDao.selectById(Integer.parseInt(blogId));if (blog == null) {System.out.println("当前 blogId = " + blogId + " 对应的博客不存在!");}String respJson = objectMapper.writeValueAsString(blog);resp.setContentType("application/json; charset=utf8");resp.getWriter().write(respJson);}}
}

在这里插入图片描述
这是因为浏览器自身也有缓存机制.
浏览器从服务器这边获取页面,这个操作是通过网络传输完成的,速度是比较慢的.浏览器就会把页面给缓存到本地(客户端电脑的硬盘上).后续再访问同一个页面,就直接读缓存.但是这样做有一些问题,即客户端命中缓存之后就不一定能及时感知到变化.
解决方法:强制刷新.(无视缓存,100%重新访问服务器)Ctrl + f5
在这里插入图片描述

3. 博客登录页

在这里插入图片描述
在此处输入用户名和密码,点击登录,就会触发一个http请求.
服务器验证用户名和密码,然后就可以根据结果,判定是否登录成功.

分为以下几个步骤:

  1. 约定前后端交互接口
  2. 实现前端代码
  3. 实现后端代码
  1. 约定前后端交互接口
    请求:
    POST/login
    username = zhangsan&password = 123
    响应:
    HTTP/1.1 302
    Location:blog_list.html

注意:
此处的响应应是from表单(本身就会触发页面跳转),响应是302才能够进行页面跳转.
如果是ajax请求(本身不会触发),响应是302,此时是无法进行跳转的.

  1. 实现前端代码
    往页面上加入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/login.css">
</head>
<body><!-- 导航栏 --><div class="nav"><img src="image/logo2.jpg" alt=""><span class="title">我的博客系统</span><!-- 用于占位,将a标签挤到右侧去 --><div class="spacer"></div><a href="#">主页</a><a href="#">写博客</a></div><!-- 正文部分 --><!-- 贯穿整个页面的容器 --><div class="login-container"><!-- 垂直水平登录的对话框 --><div class="login-dialog"><form action="login" method="post"><h3>登录</h3><div class="row"><span>用户名:</span><input type="text" id="username" placeholder="手机号/邮箱号" name="username"></div><div class="row"><span>密码:</span><input type="password" id="password" placeholder="请输入密码" name="password"></div><div class="row"><input type="submit" id="submit" value="登录"></div></form></div></div>
</body>
</html>

在这里插入图片描述
注意此处对应的关系:
在这里插入图片描述
3. 修改后端代码

package api;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 {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//设置请求的编码,使用 utf8 理解请求req.setCharacterEncoding("utf8");//设置响应的编码,使用 utf8 构造响应//resp.setCharacterEncoding("utf8");resp.setContentType("text/html;charset=utf8");//1.读取参数中的用户名和密码String username = req.getParameter("username");String password = req.getParameter("password");if(username == null ||"".equals(username) || password == null ||"".equals(password)){//登录失败String html = "<h3>登录失败!用户名/密码为空!</h3>";resp.getWriter().write(html);return;}//2.读取数据库,查看用户名是否存在.密码是否匹配UserDao userDao = new UserDao();User user =userDao.selectByUsername(username);if(user == null){//用户不存在String html = "<h3>登录失败!用户名或密码错误</h3>";resp.getWriter().write(html);return;}if(!password.equals(user.getPassword())){String html = "<h3>登录失败!用户名或密码错误</h3>";resp.getWriter().write(html);return;}//3.用户名密码验证通过,登录成功,接下来就创建会话,使用该会话保存用户信息HttpSession session = req.getSession();session.setAttribute("user",user);//4.重定向,跳转到博客列表页resp.sendRedirect("blog_list.html");}
}

在这里插入图片描述

4. 检测登录状态

实现让页面强制要求登录,
当用户访问博客列表页/详情页/编辑页,要求用户必须是已经登录的状态.如果用户还没登录,就会强制跳转到登录页面.

实现思路:
在页面加载的时候,专门发起一个新的 ajax . (一个页面里可以发N个ajax请求)以博客列表页为例,先会发一个请求获取博客列表,再发个ajax获取用户的登录状态,如果用户已登录,相安无事.如果未登录,则页面跳转到登录页.

  1. 约定前后端交互接口
  2. 实现前端代码
  3. 实现后端代码
  1. 约定前后端交互接口
    请求:GET/login
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘xxxflower’
    }

响应就把当前登录的用户信息返回回来了,如果未登录,就返回一个userld0user 对象

  1. 实现后端代码
@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.setContentType("application/json; charset=utf8");// 使用这个方法来获取到用户的登录状态.// 如果用户未登录, 这里的会话就拿不到!!HttpSession session = req.getSession(false);if (session == null) {// 未登录, 返回一个空的 user 对象User user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}User user = (User) session.getAttribute("user");if (user == null) {user = new User();String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);return;}// 确实成功取出了 user 对象, 就直接返回即可.String respJson = objectMapper.writeValueAsString(user);resp.getWriter().write(respJson);}
  1. 开发后端代码:
function checkLogin() {$.ajax({type: 'get',url: 'login',success: function(body) {if (body.userId && body.userId > 0) {// 登录成功!!console.log("当前用户已经登录!!");h3.innerHTML = body.username;} else {// 当前未登录// 强制跳转到登录页. location.assign('login.html');}}});}checkLogin();

将此代码粘贴至博客列表页
则效果如下:
在这里插入图片描述

5. 实现显示用户信息的功能

在这里插入图片描述
此处我们是写死的,我们希望这个地方可以动态生成.

  1. 如果是博客列表页,此处显示登陆用户的信息
  2. 如果此处是博客详情页,此时显示的是该文章的作者
  1. 约定前后端接口
    博客列表页:(复用监测登录状态的接口)
    请求:
    GET /login
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘huang’,
    password:‘234’
    }
    由于是在监测登录的接口中直接写,所以后端代码不变,只需要微调前端代码即可
    在这里插入图片描述
    登录可以看到:
    在这里插入图片描述
    博客详情页:
  2. 前后端交互接口
    请求:
    GET/author?blogId = 1
    响应:
    HTTP/1.1 200 OK
    {
    userId:1,
    username:‘huang’,
    password:‘234’
    }
  3. 后端代码:
package api;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;@WebServlet("/author")
public class AuthorServlet extends HttpServlet {private ObjectMapper objectMapper = new ObjectMapper();@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String blogId = req.getParameter("blogId");if(blogId == null){resp.setContentType("text/html;charset = utf8");resp.getWriter().write("参数非法,缺少blogId!");return;}//根据 blog 查询Blog对象BlogDao blogDao = new BlogDao();Blog blog = blogDao.selectById(Integer.parseInt("blogId"));if(blog == null){resp.setContentType("text/html;charset = utf8");resp.getWriter().write("没有找到指定博客");return;}//根据BLog中UserId 获取到对应的用户信息UserDao userDao = new UserDao();User author = userDao.selectById(blog.getUserId());String respJson = objectMapper.writeValueAsString(author);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}
  1. 前端代码:
function getAuthor(){$.ajax({type:'get',url:'author' + location.search,success: function(body){//将username设置到页面上let h3 = document.querySelector('.container-left .card h3');h3.innerHTML = body.userId;}})}getAuthor();

在这里插入图片描述

6. 退出登录状态

判断登录状态:

  1. 看是否能查到http session对象
  2. session对象里面有没有user

在这里插入图片描述

实现退出登录:

  1. 处理HttpSession
    getSession能够创建/获取会话,但是没有删除会话的方法.但是我们可以通过设置过期时间来达到类似效果
  2. 处理user(推荐) 可以通过removeAttribute来处理
  1. 约定前后端接口
    请求:
    GET/logout
    响应:
    HTTP/1.1 302
    Location:login.html

  2. 实现后端代码:

package api;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("/logout")
public class LogoutServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {HttpSession httpSession = req.getSession(false);if(httpSession == null){//未登录,直接出错resp.setContentType("text/html;charset=utf8");resp.getWriter().write("当前未登录!");return;}httpSession.removeAttribute("user");resp.sendRedirect("login.html");}
}
  1. 实现前端代码
    修改博客列表页 ,博客编辑页的前端代码:
    在这里插入图片描述
    在这里插入图片描述

7. 发布博客

  1. 约定前后端交互接口
    使用form表单:页面中更多了form标签,同时让form里面能够感知到博客的内容.
    请求:POST/blog
    title=标题&content=正文
    响应:HTTP/1.1 302
    Location:blog_list.html
  2. 编写服务器代码
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 发布博客// 读取请求, 构造 Blog 对象, 插入数据库中即可!!HttpSession httpSession = req.getSession(false);if (httpSession == null) {resp.setContentType("text/html; charset=utf8");resp.getWriter().write("当前未登录, 无法发布博客!");return;}User user = (User) httpSession.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 blog = new Blog();blog.setTitle(title);blog.setContent(content);blog.setUserId(user.getUserId());// 发布时间, 在 java 中生成 / 数据库中生成 都行blog.setPostTime(new Timestamp(System.currentTimeMillis()));// 插入数据库BlogDao blogDao = new BlogDao();blogDao.add(blog);// 跳转到博客列表页resp.sendRedirect("blog_list.html");}
  1. 客户端代码:
    <!-- 编辑区的容器 --><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><!-- 博客编辑器, 这里用 id 是为了和 markdown 编辑器对接, 而设置的 --><div id="editor"><textarea name="content" style="display:none"></textarea></div></form></div>

在这里插入图片描述
后续点击submit就能自动提交
display:none是隐藏元素.
在这里插入图片描述
在这里插入图片描述

相关文章:

【JavaEE初阶】博客系统后端

文章目录 一. 创建项目 引入依赖二. 设计数据库三. 编写数据库代码四. 创建实体类五. 封装数据库的增删查改六. 具体功能书写1. 博客列表页2. 博客详情页3. 博客登录页4. 检测登录状态5. 实现显示用户信息的功能6. 退出登录状态7. 发布博客 一. 创建项目 引入依赖 创建blog_sy…...

day51-Mybatis-Plus/代码生成器

1.Mybatis-Plus 定义&#xff1a;是一个Mybatis的增强工具&#xff0c;只在Mybatis基础上增强不做改变&#xff0c;简化开发&#xff0c;提升效率 2.MP实战 2.1 创建springboot工程&#xff0c;勾选web&#xff0c;引入依赖 <dependency> <groupId>mysql<…...

22.Netty源码之解码器

highlight: arduino-light 抽象解码类 https://mp.weixin.qq.com/s/526p5f9fgtZu7yYq5j7LiQ 解码器 Netty 常用解码器类型&#xff1a; ByteToMessageDecoder/ReplayingDecoder 将字节流解码为消息对象&#xff1b;MessageToMessageDecoder 将一种消息类型解码为另外一种消息类…...

R语言【Tidyverse、Tidymodel】的机器学习方法

机器学习已经成为继理论、实验和数值计算之后的科研“第四范式”&#xff0c;是发现新规律&#xff0c;总结和分析实验结果的利器。机器学习涉及的理论和方法繁多&#xff0c;编程相当复杂&#xff0c;一直是阻碍机器学习大范围应用的主要困难之一&#xff0c;由此诞生了Python…...

vscode 第一个文件夹在上一层文件夹同行,怎么处理

我的是这样的 打开终端特别麻烦 解决方法就是 打开vscode里边的首选项 进入设置 把Compact Folders下边对勾给勾掉...

[JavaScript游戏开发] 绘制冰宫宝藏地图、人物鼠标点击移动、障碍检测

系列文章目录 第一章 2D二维地图绘制、人物移动、障碍检测 第二章 跟随人物二维动态地图绘制、自动寻径、小地图显示(人物红点显示) 第三章 绘制冰宫宝藏地图、人物鼠标点击移动、障碍检测 第四章 绘制Q版地图、键盘上下左右地图场景切换 文章目录 系列文章目录前言一、本章节…...

【NLP概念源和流】 01-稀疏文档表示(第 1/20 部分)

一、介绍 自然语言处理(NLP)是计算方法的应用,不仅可以从文本中提取信息,还可以在其上对不同的应用程序进行建模。所有基于语言的文本都有系统的结构或规则,通常被称为形态学,例如“跳跃”的过去时总是“跳跃”。对于人类来说,这种形态学的理解是显而易见的。 在这篇介…...

服务器运行python程序的使用说明

服务器的使用与说明 文章目录 服务器的使用与说明1.登录2.Python的使用2.1 服务器已安装python32.2 往自己的用户目录安装python31.首先下载安装包2.解压缩3.编译与安装 2.3 新建环境变量2.4 测试 3 创建PBS作业并提交 1.登录 windowsr打开运行命令窗口&#xff0c;在运行框中…...

8.2一日总结

1.记录更新&#xff1a; untracked&#xff1a; 未追踪&#xff08;新增的文件&#xff09; unmodefied&#xff1a; 未修改 modefied&#xff1a; 已修改 staged&#xff1a; 已暂存 2、添加指定文件到暂存区&#xff1a; git add 文件名 gi…...

JavaScript(四)DOM及CSS操作

1、DOM简介 DocumentType: Html的声明标签 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Docume…...

window中,关闭java占用端口的进程

查看端口被占用的情况 netstat -ano|findstr "端口号"使用Tasklist查看对于 PID 的进程名 tasklist|findstr "PID号"通过 taskkill 命令方式结束进程 taskkill /f /t /im Pid...

【Python】PySpark 数据计算 ⑤ ( RDD#sortBy方法 - 排序 RDD 中的元素 )

文章目录 一、RDD#sortBy 方法1、RDD#sortBy 语法简介2、RDD#sortBy 传入的函数参数分析 二、代码示例 - RDD#sortBy 示例1、需求分析2、代码示例3、执行结果 一、RDD#sortBy 方法 1、RDD#sortBy 语法简介 RDD#sortBy 方法 用于 按照 指定的 键 对 RDD 中的元素进行排序 , 该方…...

Elasticsearch官方测试数据导入

一、数据准备 百度网盘链接 链接&#xff1a;https://pan.baidu.com/s/1rPZBvH-J0367yQDg9qHiwQ?pwd7n5n 提取码&#xff1a;7n5n文档格式 {"index":{"_id":"1"}} {"account_number":1,"balance":39225,"firstnam…...

uniapp项目的pdf文件下载与打开查看

最近写的uniapp项目需要新增一个pdf下载和打开查看功能&#xff0c;摸索了半天终于写了出来&#xff0c;现分享出来供有需要的同行参考&#xff0c;欢迎指正 async function DownloadSignature() {//请求后端接口&#xff0c;返回值为一个url地址let resawait req.flow.flowDo…...

DeepVO 论文阅读

论文信息 题目&#xff1a;DeepVO Towards End-to-End Visual Odometry with Deep Recurrent Convolutional Neural Networks 作者&#xff1a;Sen Wang, Ronald Clark, Hongkai Wen and Niki Trigoni 代码地址&#xff1a;http://senwang.gitlab.io/DeepVO/ (原作者并没有开源…...

HOT71-字符串解码

leetcode原题链接: 字符串解码 题目描述 给定一个经过编码的字符串&#xff0c;返回它解码后的字符串。 编码规则为: k[encoded_string]&#xff0c;表示其中方括号内部的 encoded_string 正好重复 k 次。注意 k 保证为正整数。你可以认为输入字符串总是有效的&#xff1b;输…...

redis-server进程无法关闭终极解决方案

先使用命令查看6379端口情况&#xff1a; sudo lsof -i :6379 发现redis进程在占用&#xff0c;redis-server进程无论什么手段都杀不死&#xff0c;使用kill -9 pid杀掉pid后又卷土重来&#xff0c;最后找到了下面这个命令 sudo /etc/init.d/redis-server stop ok&#xff0c…...

(5)将固件加载到没有ArduPilot固件的主板上

文章目录 前言 5.1 下载驱动程序和烧录工具 5.2 下载ArduPilot固件 5.3 使用测试版和开发版 5.3.1 测试版 5.3.2 最新开发版本 5.4 将固件上传到自动驾驶仪 5.5 替代方法 5.6 将固件加载到带有外部闪存的主板上 前言 ArduPilot 的最新版本&#xff08;Copter-3.6, Pl…...

wpf画刷学习1

在这2篇博文有提到wpf画刷&#xff0c; https://blog.csdn.net/bcbobo21cn/article/details/109699703 https://blog.csdn.net/bcbobo21cn/article/details/107133703 下面单独学习一下画刷&#xff1b; wpf有五种画刷&#xff0c;也可以自定义画刷&#xff0c;画刷的基类都…...

Opencv C++实现yolov5部署onnx模型完成目标检测

代码分析&#xff1a; 头文件 #include <fstream> //文件 #include <sstream> //流 #include <iostream> #include <opencv2/dnn.hpp> //深度学习模块-仅提供推理功能 #include <opencv2/imgproc.hpp> //图像处理模块 #include &l…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

基于FPGA的PID算法学习———实现PID比例控制算法

基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容&#xff1a;参考网站&#xff1a; PID算法控制 PID即&#xff1a;Proportional&#xff08;比例&#xff09;、Integral&#xff08;积分&…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

浪潮交换机配置track检测实现高速公路收费网络主备切换NQA

浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求&#xff0c;本次涉及的主要是收费汇聚交换机的配置&#xff0c;浪潮网络设备在高速项目很少&#xff0c;通…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

【Linux系统】Linux环境变量:系统配置的隐形指挥官

。# Linux系列 文章目录 前言一、环境变量的概念二、常见的环境变量三、环境变量特点及其相关指令3.1 环境变量的全局性3.2、环境变量的生命周期 四、环境变量的组织方式五、C语言对环境变量的操作5.1 设置环境变量&#xff1a;setenv5.2 删除环境变量:unsetenv5.3 遍历所有环境…...

MySQL的pymysql操作

本章是MySQL的最后一章&#xff0c;MySQL到此完结&#xff0c;下一站Hadoop&#xff01;&#xff01;&#xff01; 这章很简单&#xff0c;完整代码在最后&#xff0c;详细讲解之前python课程里面也有&#xff0c;感兴趣的可以往前找一下 一、查询操作 我们需要打开pycharm …...

小木的算法日记-多叉树的递归/层序遍历

&#x1f332; 从二叉树到森林&#xff1a;一文彻底搞懂多叉树遍历的艺术 &#x1f680; 引言 你好&#xff0c;未来的算法大神&#xff01; 在数据结构的世界里&#xff0c;“树”无疑是最核心、最迷人的概念之一。我们中的大多数人都是从 二叉树 开始入门的&#xff0c;它…...