【Java项目】完善基于Java+MySQL+Tomcat+maven+Servlet的博客系统
创始人
2025-05-30 18:12:13

目录

  • 一、准备工作
  • 二、引入依赖
  • 三、创建必要的目录
  • 四、编写代码
  • 五/六、打包部署(直接基于 smart tomcat)
  • 七、验证代码
  • 正式编写服务器代码
    • 编写数据库相关的操作代码
    • 创建数据库/表结构(数据库设计)
      • 数据库代码
      • 封装数据库操作
      • 封装针对数据的增删改查!
    • 博客列表页
      • 约定前后端接口
      • 编写服务器代码
      • 编写客户端代码
    • 问题一:
    • 问题二:刷新页面的时候,发现一哆嗦~
    • 博客详情页
      • 约定前后端交互接口
      • 前端代码
    • 登录页
      • 约定前后端交互接口
      • 后端逻辑
      • 约定前后端交互接口
    • 正确显示用户信息
      • 针对博客列表页
      • 针对博客详情页
    • 实现"注销"功能
      • 约定前后端交互接口
    • 发布博客功能
      • 约定前后端交互接口
    • 删除博客
  • 完整代码

一、准备工作

打开你的idea,创建一个Maven。

  1. File->new->Project;
    在这里插入图片描述

  2. 选择创建maven项目,然后点击next
    在这里插入图片描述

  3. 取个名字
    在这里插入图片描述

二、引入依赖

这里我们需要引入的依赖是servlet,jackson,mysql。
这个操作嘛!直接看JavaEE-实现一个服务器版本的“表白墙”
里面有引入依赖的相关操作~

pox.mal


4.0.0org.exampleblog_system1.0-SNAPSHOT88javax.servletjavax.servlet-api3.1.0providedcom.fasterxml.jackson.corejackson-databind2.12.6.1mysqlmysql-connector-java5.1.47

三、创建必要的目录

点开我们项目下的src文件,找到main,然后右键创建一个新的目录,webapp;在webapp下创建一个目录叫做WEB-INF,然后在WEB-INF下面创建一个新的xml文件叫做web.xml
在这里插入图片描述
在这里插入图片描述
在web.xml文件中填写以下代码

Archetype Created Web Application

四、编写代码

找到java文件,在java文件下创建一个类叫做HelloServlet
在这里插入图片描述
写一个简单的实验代码

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;public class HelloServlet extends HttpServlet {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {resp.getWriter().write("你好,博客系统!");}
}

五/六、打包部署(直接基于 smart tomcat)

这里打包部署在博主之前的博客已经介绍过,使用idea自带的插件smart tomcat来完成
在这里插入图片描述
在这里插入图片描述

七、验证代码

在浏览器验证程序。

正式编写服务器代码

在这里插入图片描述
找到我们以前的博客系统的前端代码,然后复制粘贴到当前的webapp目录下
在这里插入图片描述
在这里插入图片描述
就可以把前端页面给引入项目中。
在这里插入图片描述

C和M,先来实现Model层~~ 先来实现数据库相关的代码

编写数据库相关的操作代码

创建数据库/表结构(数据库设计)

设计数据库,需要根据当前的需求来进行设计~~

这里来介绍一下前面写的博客系统页面:
1.博客列表页:显示博客的列表;
2.博客详情页,点击博客列表页,上面列出的博客篇数,可以跳转到完整的博客内容;
3.登录页面;
4.博客编辑页,基于editor.md整了一个markdown编辑器,根据这个页面来发布博客

存储博客~当点击发布的时候,博客被发布到服务器上,就要被存起来
获取博客~当博客列表页和博客详情页,能够拿到博客的内容,还能够进行登录校验

设计表,就需要抓住需求中的实体(关键性的名词)

博客表,用来存储所有博客数据~
用户表,用户登录,就需要用到这个表~

数据库代码

-- 编写建库建表的 sqlcreate database if not exists java107_blog;use java107_blog;-- 创建一个博客表
drop table if exists blog;
create table blog (blogId int primary key auto_increment,title varchar(1024),content mediumtext,userId int,         -- 文章作者的 idpostTime datetime   -- 发布时间
);-- 创建一个用户表
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');522215

编写完成之后,打开mysql客户端,然后复制上面的SQL语句,粘贴到我们的mysql客户端
在这里插入图片描述
检查数据库和表是否创建完成
在这里插入图片描述

封装数据库操作

先创建DBUtil封装数据库连接的操作

import com.mysql.jdbc.jdbc2.optional.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/java107_blog?characterEncoding=utf8&&useSSL=false";private static final String USERNAME = "root";private static final String PASSWORD = "1234";private static volatile 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 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);}}}}

创建实体类!
使用实体类表示数据库中的一条记录

此处主要创建了Blog类和User类
User类

// 每个 User 对象,对应 user 表里的一条记录
public class User {private int userId;private String username;private String password;public int getUserId() {return userId;}public void setUserId(int userId) {this.userId = userId;}public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}
}

Blog类

import java.sql.Timestamp;// 每个 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;}public Timestamp getPostTime() {return postTime;}public void setPostTime(Timestamp postTime) {this.postTime = postTime;}
}

封装针对数据的增删改查!

把提供了增删改查这样的类,称为DAO
BlogDao类

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 e) {throw new RuntimeException(e);} finally {// 4) 关闭连接,释放资源DBUtil.close(connection, statement, null);}}// 2. 能够获取到博客表中的所有博客表的信息(用于博客列表页),此处每篇博客不一定获取到完整的正文public List selectAll() {List blogs = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from blog";statement = connection.prepareStatement(sql);resultSet = statement.executeQuery();while (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);} 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 {connection = DBUtil.getConnection();String sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);resultSet = 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 e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}// 4. 从博客表中,根据博客id删除博客.public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);statement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, null);}}// 注意,上述操作是增删查,没有改~}

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 {connection = DBUtil.getConnection();String sql = "select * from user where username = ?";statement = connection.prepareStatement(sql);statement.setString(1, username);resultSet = statement.executeQuery();// 此处 username 使用了 unique 约束,要么能查到一个,要么一个都查不到if (resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}// 2. 根据用户 id 来找用户信息//    博客详情页,就可以根据用户 id 来查询作者的名字,把作者名字显示出来。public User selectById(int userId) {Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from user where userId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, userId);resultSet = statement.executeQuery();// 此处 username 使用了 unique 约束,要么能查到一个,要么一个都查不到if (resultSet.next()) {User user = new User();user.setUserId(resultSet.getInt("userId"));user.setUsername(resultSet.getString("username"));user.setPassword(resultSet.getString("password"));return user;}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}
}

结过上述的逻辑,终于把数据库操作都准备好了~
接下来,就可以实现服务器中的后续代码了~
(Model就搞定了,要去写Controller了)



针对这里的这四个页面
分别来“约定前后端交互接口”,“编写服务器代码”,“编写客户端代码”

博客列表页

这个页面需要能够展示出数据库中的博客的列表

约定前后端接口

请求:
GET /blog响应:
[{blogId: 1,title: ' 这是第一篇博客',content: '这是博客正文',userId: 1,postTime: '2023-03-18  09:00:00'},{blogId: 2,title: ' 这是第二篇博客',content: '这是博客正文',userId: 1,postTime: '2023-03-18  09:00:00'},
]

这里的content与其说是“正文”,不如说是正文的摘要。
博客列表里面,主要显示有哪些博客~
因此,就需要在这个地方把正文进行截取(如果正文太长,就只截取前面的一部分)

编写服务器代码

接下来进一步编写服务器和客户端之间的代码~
我们试着先来写一下,看一下是否能连接成功!

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;// 通过这个类,来处理 /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 blogs = blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson = objectMapper.writeValueAsString(blogs);resp.setContentType("application/json;charset=utf8");resp.getWriter().write(respJson);}
}

在这里插入图片描述
发现连接成功了~

在数据库中插入点信息

-- 给博客表中插入数据,方便测试。
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());

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
此处得到的响应,和预期的效果,有一点点差别,时间格式~
预期得到的是一个格式化时间,实际上得到一个毫秒时间戳
此处就需要把时间戳转成格式化时间(可以在前端来做,也可以在后端来做)
这里在后端来改!
在Blog类中:

public String getPostTime() {// 使用 SimpleDateFormat 来完成时间戳到格式化日期的转换// 这个转换过程,需要在构造方法中指定要转换格式,然后调用format来进行转换SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return simpleDateFormat.format(postTime);}

在这里插入图片描述
这里就看到我们的格式改变了~

编写客户端代码

在页面加载的时候,让页面通过ajax访问服务器,获取到数据库中的博客数据,并且填写到页面中!

处理服务器响应的逻辑:


完整代码:



博客列表页

我的博客系统
主页写博客注销

摸鱼王胖嘟嘟

gitee 地址
文章分类
21
我的第一篇博客
2023-02-11 18:26:00
从今天起,我要认真敲代码. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque exercitationem, ut doloribus blanditiis, eveniet earum culpa accusamus rem eum cum deleniti, quisquam expedita distinctio tempora quaerat adipisci officia esse reprehenderit.
查看全文 >
我的第一篇博客
2023-02-11 18:26:00
从今天起,我要认真敲代码. Lorem, ipsum dolor sit amet consectetur adipisicing elit. Neque exercitationem, ut doloribus blanditiis, eveniet earum culpa accusamus rem eum cum deleniti, quisquam expedita distinctio tempora quaerat adipisci officia esse reprehenderit.
查看全文 >>

BlogDao.java逻辑的处理

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 e) {throw new RuntimeException(e);} finally {// 4) 关闭连接,释放资源DBUtil.close(connection, statement, null);}}// 2. 能够获取到博客表中的所有博客表的信息(用于博客列表页),此处每篇博客不一定获取到完整的正文public List selectAll() {List blogs = new ArrayList<>();Connection connection = null;PreparedStatement statement = null;ResultSet resultSet = null;try {connection = DBUtil.getConnection();String sql = "select * from blog";statement = connection.prepareStatement(sql);resultSet = statement.executeQuery();while (resultSet.next()) {Blog blog = new Blog();blog.setBlogId(resultSet.getInt("blogId"));blog.setTitle(resultSet.getString("title"));blog.setContent(resultSet.getString("content"));blog.setUserId(resultSet.getShort("userId"));blog.setPostTime(resultSet.getTimestamp("postTime"));blogs.add(blog);}} catch (SQLException e) {throw new RuntimeException(e);} 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 {connection = DBUtil.getConnection();String sql = "select * from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);resultSet = statement.executeQuery();// 此处我们是使用 主键 来作为查询条件的,查询结果,要么是1,要么是0if (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"));return blog;}} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, resultSet);}return null;}// 4. 从博客表中,根据博客id删除博客.public void delete(int blogId) {Connection connection = null;PreparedStatement statement = null;try {connection = DBUtil.getConnection();String sql = "delete from blog where blogId = ?";statement = connection.prepareStatement(sql);statement.setInt(1, blogId);statement.executeUpdate();} catch (SQLException e) {throw new RuntimeException(e);} finally {DBUtil.close(connection, statement, null);}}// 注意,上述操作是增删查,没有改~
}

问题一:

当前这个博客列表页,还有点小问题~
当前拿到的博客列表顺序,是不太科学的

在这里插入图片描述
需要保证我们最新的博客得在最上面才行~

如果在sql中没有指定 order by ~ 此时查询到的结果是不确定的~

因此,在sql中不加 order by 之前,是不应该依赖查询结果的顺序的~

在BlogDao.java中找到此处
在这里插入图片描述
可以看到是降序排序的了

在这里插入图片描述

问题二:刷新页面的时候,发现一哆嗦~

旧的内容(测试时写死的数据) => 新的内容(从数据库里查的数据)
是通过网络交互的,花个几十 ms 都是很正常的,人眼是能捕捉到这个变化的~

直接把旧的测试数据删了就行了~
在这里插入图片描述
像这样~~

在这里插入图片描述

博客详情页

约定前后端交互接口

请求:
GET /blog?blogId= 1
响应:
HTTP/1.1 200 OK
Content-Type: application/json;{blogId: 1,title: “第一篇博客”,content: "这是正文",userId: 1,postTime: '2023-03-18 15:58:00'
}

和获取博客列表的区别~
1.请求里面,带有参数,blogId
2.响应结果不是json数组了,而只是单一的对象
3.响应的博客正文,不再截断了。

获取博客列表的请求
GET/ blog
不带参数的~

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;// 通过这个类,来处理 /blog 的对应请求
@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 pararm = req.getParameter("blogId");if (pararm == null) {// 不存在参数,获取博客列表// 从数据库中查询到博客列表,转成JSON格式 然后直接返回即可List blogs = blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson = objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {// 存在参数,获取博客详情int blogId = Integer.parseInt(pararm);Blog blog = blogDao.selectOne(blogId);String respJson = objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}
}

在这里插入图片描述
后端代码实现和博客列表页的获取,基本相同,就直接放到一个方法中来实现了,使用blogId参数来区分是获取博客列表还是详情~

前端代码

修改blog_detail.html,让这个页面加载的时候,能够调用上述接口,来从服务器获取到博客数据!
在前端代码这边,要想构造一个请求获取博客详情,就得知道当前用户点击的博客的id!
这个id就已经包含在 当前的 blog_detail.html页面的url里面了~
在这里插入图片描述

希望在博客详情页拿到博客的具体内容。
就需要构造一个请求 /blog?blogId=5
这个请求是 blog_detail.html通过ajax发送的
blog_detail.html就需要构造出blogId(5)的这个参数
关键问题就是这个参数(5)从哪里来的呢?
其实当来到blog_detail.html这个页面的时候,url里就已经带上了这个参数~

通过location.search就能够拿到?blogId=5这段内容,从而构造出/blog?blogId=5这样的请求。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
既然如此,那应该如何处理?能够让我们这里的markdown文本内容,被渲染成带有特定样式的html片段呢?
仍然要使用edlitor.md这个库来实现。
这个库不仅提供了markdown编辑器,也提供了渲染功能~

当我们在进行程序验证的时候,要时刻牢记,浏览器缓存可能会影响到结果.
之前已经访问过blog_detail页面了,因此浏览器就可能把这个页面给保存到本地.
下次再尝试访问的时候就直接访问本地内容了。

在这里插入图片描述



博客详情页

我的博客系统
主页写博客注销

摸鱼王胖嘟嘟

gitee 地址
文章分类
21

当前已经把博客列表页和详情页搞出来了
接下来来写登录页

登录页

约定前后端交互接口

请求:
POST /login
Content-Type: application/x-ww-form-urlencoded
这里的逻辑,可以直接使用form表单来进行提交,没必要非要使用ajax~
username=zhangsan&password=123
响应:
HTTP/1.1 302
Location:blog.list.html
当登录成功之后,就自动跳转到,主页(博客列表页)

在这里插入图片描述
1)给这部分代码套上一层form标签!
给input加上name属性
2)username=zhangsan&password=123
3)把button按钮换成input标签

在前端页面开发的过程中,html页面结构,是非常重要的,后续的CSS和JS很多都依赖了这个页面的结构
一旦页面结构发生了调整,就可能导致CSS或者js失效



登录页面

我的博客系统
主页写博客

登录

用户名
密码
/* 登录页面的专用样式文件 */.login-container {width: 100%;height: calc(100% - 50px);/* 需要让里面的子元素垂直水平居中 */display: flex;align-items: center;justify-content: center;
}.login-dialog {width: 400px;height: 350px;background-color: rgba(255, 255, 255, 0.8);border-radius: 10px;
}.login-dialog h3 {text-align: center;padding: 50px 0;
}.login-dialog .row {height: 50px;width: 100%;display: flex;align-items: center;justify-content: center;
}.login-dialog .row span {/* 把span转换成块级元素,方便设置尺寸 */display: block;width: 100px;font-weight: 700;
}#username, #password {width: 200px;height: 40px;font-size: 22px;line-height: 40px;padding-left: 10px;border-radius: 10px;/* 去掉边框 */border: none;/* 去掉轮廓线 */outline: none;
}.row #submit {width: 300px;height: 50px;border-radius: 10px;color: white;background-color: rgb(0,128,0);border: none;outline: none;margin-top: 50px;
}.row #submit:active {background-color: #666;
}       

后端逻辑

此处约定的路径是/login,这是一个新的路径,就需要使用一个新的servlet来处理
在这里插入图片描述
使用getPararmeter读取参数的时候,如果参数的值是纯英文,那还好,一般没啥问题。
如果参数的值是中文,此时直接读取参数,很容易出现乱码

中文就涉及到乱码。
在这里插入图片描述
但是Servlet默认并不是按照utf8来解析的~

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 {@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {req.setCharacterEncoding("utf8");resp.setCharacterEncoding("utf8");// 1. 获取到请求中的参数String username = req.getParameter("username");String password = req.getParameter("password");System.out.println("username=" + username + ", password=" + 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");}
}

当登录功能完成之后,就需要调整一下之前的两个页面(博客列表和博客详情)
让这两个页面,必须登录后才能访问~~
此处就做出这样的限制~(这样限制一下,后面实现一些其他功能会更简单)
在进入博客列表页/详情页的时候,先检查一下用户的登录状态~如果用户当前已经是登录状态,才能继续使用,如果是未登录状态,则强制跳转到login页面

如何实现上述功能?
可以在博客列表页/详情页,加载的时候,通过ajax访问一下服务器,获取当前的登录状态~看能不能获取到,如果获取到了,就说明当前确实是已经登录了。此时就可以留在这个页面了。
如果没有获取到,说明就未登录。就需要跳转到登录页面~

约定前后端交互接口

    请求:GET /login响应:HTTP/1.1 200 OKContent-Type:application/json{userId: 1,username: 'zhangsan',}

登录了,就直接返回当前登录的用户信息.
未登录,则直接返回一个userId为0的对象

{userId: 0,username: '',}

此处只是一种典型的约定方式,完全也可以采用其他的方式来约定~
比如使用403表示当前未登录~

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("utf8");resp.setCharacterEncoding("utf8");// 1. 获取到请求中的参数String username = req.getParameter("username");String password = req.getParameter("password");System.out.println("username=" + username + ", password=" + 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");}// 这个方法用来让前端检测当前的登录状态@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 = new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}User user = (User) session.getAttribute("user");if (user == null) {// 虽然有会话,但是会话里没有 user 对象,也视为未登录user = new User();resp.getWriter().write(objectMapper.writeValueAsString(user));return;}// 已经登录的状态!// 注意,此处不要把密码返回到前端user.setPassword("");resp.getWriter().write(objectMapper.writeValueAsString(user));}
}

前端代码:



博客列表页

我的博客系统
主页写博客注销

摸鱼王胖嘟嘟

gitee 地址
文章分类
21


博客详情页

我的博客系统
主页写博客注销

摸鱼王胖嘟嘟

gitee 地址
文章分类
21

正确显示用户信息

在这里插入图片描述
当下用户信息,显示的是”摸鱼王胖嘟嘟“,实际登录的用户,显示”zhangsan“。

针对博客列表页

在这里插入图片描述



博客列表

我的博客系统
主页写博客注销

github 地址
文章分类
21

针对博客详情页

当前看到的是,博客详情页,用户名,也是成了zhangsan了,此处就需要处理一下,让博客列表页和详情页,能够做出一些区分~

让服务器,提供一个新的接口~这个接口可以让客户端指定blogId,获取到指定blogId的作者信息!

请求:
GET/authorInfo?blogId=6响应:
{userId:2,username: 'lisi',
}

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



博客详情页

我的博客系统
主页写博客注销

github 地址
文章分类
21

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

实现"注销"功能

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

约定前后端交互接口

请求:
GET / logout
响应:
HTTP/1.1 302
Location:login.html
package controller;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;@SuppressWarnings({"all"})
@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("当前用户尚未登录,无法注销1");return;}// 然后把这个用户的会话中的信息删掉session.removeAttribute("user");resp.sendRedirect("blog_login.html");}
}

发布博客功能

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

约定前后端交互接口

请求:
POST/blog
Content-Type:application/x-www-form-urlencodedtitle=这是标题&content=这是正文....
响应:
HTTP/1.1 302
Location:blog_list.html
package controller;import com.fasterxml.jackson.databind.ObjectMapper;
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;
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 {resp.setContentType("application/json;charset=utf8");BlogDao blogDao = new BlogDao();// 先尝试获取到 req 中的 blogId参数,如果该参数存在,说明要请求博客详情// 如果不存在,就说明要请求博客列表String pararm = req.getParameter("blogId");if (pararm == null) {// 不存在参数,获取博客列表// 从数据库中查询到博客列表,转成JSON格式 然后直接返回即可List blogs = blogDao.selectAll();// 把 blogs 对象转成JSON格式String respJson = objectMapper.writeValueAsString(blogs);resp.getWriter().write(respJson);} else {// 存在参数,获取博客详情int blogId = Integer.parseInt(pararm);Blog blog = blogDao.selectOne(blogId);String respJson = objectMapper.writeValueAsString(blog);resp.getWriter().write(respJson);}}@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");}
}

这里就需要整一个form表单,把这里的内容给套上!
在这里插入图片描述
在这里插入图片描述

删除博客

在这里插入图片描述
服务器处理
用户点击删除按钮,触发一个HTTP请求,HTTP请求就会让服务器删除指定的博客~

完整代码

完整代码我放gitee仓库了,需要的可以去博客系统仓库找。

相关内容

热门资讯

一、基础算法3:二分 模板题+... 文章目录算法模板整数二分算法模板浮点数二分算法模板模板题数的范围原题链接题目题解数的三次方根原题链接...
Essential C++复习... 好久没写代码了,很多东西都忘记了。复盘一下C++编写基础头文件 与 输...
三十五、DRF中的过滤、Dja... 一、DRF中的过滤 REST framework 的通用列表视图的默认行为是返回模型管理器的整个查询...
基于RZ/G2UL Corte... 以太网接口是一种广泛应用的网络接口,它可以在不同的场合实现不同的功能。例如࿰...
nginx-会话保持-3 基于LNMP在负载均衡集群上部署wordpress(低配版) 在讲解会话...
【BBuf的CUDA笔记】九,... 0x0. 背景 随着年纪越来越大,读代码越来越困难,如果你发现看不懂同事...
springboot: myb... 目录 需求01:  根据不同类型 查询不同的订单名, 1. 书写订单 类型转换方法   2. 使用方...
最前端|什么是低代码?与传统开... 目录一、低代码介绍二、背景趋势三、低代码与传统代码开发(一)低代码能否替...
物流成本总是超标?一招教你降本... 先来看一个数据,中国仓储物流管理指标与世界标杆数据对比 第三方物流比例:...
exec家族与system函数 exec家族函数NAMEexecl, execlp, execle, execv, execvp, ...
Pots 倒水问题 简单搜索&&进阶搜索 - Virtual Judge (vjudge.net) 【题目描述】 两个...
Linux内核通杀提权漏洞(C... 之前Linux官方爆出了"脏牛"漏洞(代号:Dirty COW,漏洞编号...
如何解决“sxs.dll错误”... 在使用计算机的过程中,可能会遇到各种各样的错误。其中一个常见的错误是“sxs.dll错...
【数据结构】夯实基础|线性表刷... 作者:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享...
自定义类型【C进阶】 文章目录😺结构体🟥结构体内存对齐内存对齐的规则内存对齐的意义修改默认...
latex 知识点总结 文章目录(一)latex 知识点总结(二)l...
【小猫爪】AUTOSAR学习笔... 【小猫爪】AUTOSAR学习笔记15-BswM模块前言1 BswM模块简介2 BswM功能简介2.1...
PMSM矢量控制笔记(1.2)... 前言:永磁同步电机的转子包括永磁体、转子铁芯、轴承等机械结构等,其转子可...
关于面试时HA(RAC)会问到... 1.什么是RAC(Real Application Cluster)&#...
关于车身广告的规定(车身广告是... 今天给各位分享关于车身广告的规定的知识,其中也会对车身广告是否合法进行解释,如果能碰巧解决你现在面临...
尾灯熏黑是怎么弄的?(尾灯熏黑... 本篇文章极速百科给大家谈谈尾灯熏黑是怎么弄的?,以及尾灯熏黑后怎么弄掉对应的知识点,希望对各位有所帮...
2030年汽油车就不能开了吗?... 本篇文章极速百科给大家谈谈2030年汽油车就不能开了吗?汽油车什么时候淘汰,以及2025汽油车会淘汰...
法拉利各个款式的红色跑车你钟爱... 今天给各位分享法拉利各个款式的红色跑车你钟爱哪一款的知识,其中也会对法拉利红色车多少钱一辆进行解释,...
一尺等于多少厘米(一尺等于多少... 本篇文章极速百科给大家谈谈一尺等于多少厘米,以及一尺等于多少厘米长度对应的知识点,希望对各位有所帮助...
mg是什么意思单位(mg是什么... 今天给各位分享mg是什么意思单位的知识,其中也会对mg是什么单位g又是什么单位进行解释,如果能碰巧解...
瑞纳1.4手动真实油耗-百度有... 本篇文章极速百科给大家谈谈瑞纳1.4手动真实油耗-百度有驾,以及瑞纳14l油耗多少真实油耗对应的知识...
2012款福克斯按键介绍(20... 今天给各位分享2012款福克斯按键介绍的知识,其中也会对2012款福克斯按键功能介绍进行解释,如果能...
轻松缓存 Android + ... 轻松缓存 Android + Kotlin + Flow 技术背景 在某些情况下&...
搜索系统(二) term weight 如何衡量一个词在一篇文档中的重要性 词频率(tf)...
基于“遥感+”融合技术在碳储量... 以全球变暖为主要特征的气候变化已成为全球性环境问题,对全球可持续发展带来严峻挑战。20...