目录
3. 针对操作数据库创建Dao类(BlogDao和UserDao(重要))
4.5 服务器端删除博客代码:(删除本身在博客详情页当中体现)
4.7 服务器端注销业务代码:(注销本身在博客列表页当中体现)
0. 项目结构
0.1 单元测试目录
1. 项目的准备工作
1. 搭建一个空的maven项目(之前有博客已经写了)
2. 对maven进行基本的配置
2.1 首先在目录 src/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>
2.2 配置maven 的核心页 pom.xml
分四步:
a)引入依赖,分别有servlet,mysql,thymeleaf
<dependencies>
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.thymeleaf/thymeleaf -->
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.12.RELEASE</version>
</dependency>
</dependencies>
b)配置打包jar包改为war包(maven识别的是war包)
<packaging>war</packaging>
c)修改war包名字
<build>
<finalName>rocket_blog</finalName>
</build>
d)设置maven用的jdk版本
<properties>
<encoding>UTF-8</encoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
这样maven基本配置就完毕了
2. 数据库设计
在 src/main 目录下创建 db.sql 文件
写入以下代码用于创建数据库和表:
需要两张表:博客表(博客ID,标题,正文,作者的用户ID,发布时间)
用户表(用户ID,用户名,密码)
create database if not exists RocketBlog;
use RocketBlog;
drop table if exists blog;
create table blog (
blogID int primary key auto_increment,
title varchar(512),
content text,
userId int,
postTime datetime
);
drop table if exists user;
create table user (
userId int primary key auto_increment,
username varchar(50),
password varchar(50)
);
然后后把代码复制进mysql:
查看一下创建的表:有2张,分别为blog和user
看一下表的详细内容:
3. 把JDBC操作数据库的代码进行封装
1.封装一下数据库建立连接的过程(单例)
在main/java文件夹下创建DBUtil文件
代码如下:DBUtil和数据库建立连接/收尾工作(重要)
1.初始化属性
2.获取数据源
3.获取数据库连接
4.关闭资源
public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/RocketBlog?characterEncoding=utf8&useSSL=false";
private static final String USERNAME = "root";
private static final String PASSWORD = "2222";
private static DataSource dataSource = null;
public static DataSource getDataSource() { //获取数据源
// 看一下 dataSource 当前是否已经持有一个实例了.
// 如果没有持有, 就创建一个新的.
// 如果持有了, 就不必创建新的, 直接返回之前的.
if (dataSource == null) {
MysqlDataSource mysqlDataSource = new MysqlDataSource();
mysqlDataSource.setUrl(URL);
mysqlDataSource.setUser(USERNAME);
mysqlDataSource.setPassword(PASSWORD);
dataSource = mysqlDataSource;
}
return dataSource;
}
public static Connection getConnection() { //获取数据库连接
try {
return getDataSource().getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null;
}
public static void close(Connection connection,
PreparedStatement statement,
ResultSet resultSet) { //关闭资源
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
2. 创建实体类User、Blog
在main/java文件夹下创建Blog和User
User:
public class User {
private int userId;
private String username;
private String password;
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "dao.User{" +
"userId=" + userId +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
Blog:
public class Blog {
private int blogId;
private String title;
private String content;
private int userId;
// java.sql.Date 这个类型只能表示日期, 没有时分秒
private Timestamp postTime;
public int getBlogId() {
return blogId;
}
public void setBlogId(int blogId) {
this.blogId = blogId;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getUserId() {
return userId;
}
public void setUserId(int userId) {
this.userId = userId;
}
public Timestamp getPostTime() {
return postTime;
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
@Override
public String toString() {
return "dao.Blog{" +
"blogId=" + blogId +
", title='" + title + '\'' +
", content='" + content + '\'' +
", userId=" + userId +
", postTime=" + postTime +
'}';
}
}
3. 针对操作数据库创建Dao类(BlogDao和UserDao(重要))
在main/java文件夹下创建BlogDao和UserDao
BlogDao:
public class BlogDao {
// 往数据库中新增一个 博客
public void insert(Blog blog) {
// 1. 和数据库服务器建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "insert into blog values(null, ?, ?, ?, now())";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1, blog.getTitle());
statement.setString(2, blog.getContent());
statement.setInt(3, blog.getUserId());
// 3. 执行 SQL
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 4. 收尾工作.
DBUtil.close(connection, statement, null);
}
}
// 从数据库删除博客
public void delete(int blogId) {
// 1. 和数据库服务器建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "delete from blog where blogId = ?";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setInt(1, blogId);
// 3. 执行 SQL
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, null);
}
}
// 从数据库中查找出所有的博客.
// 实现博客列表页的时候, 就需要这个方法.
// 如果博客数目少, 都无所谓. 如果博客数目很多, 应该要支持 "分页查询"
// limit offset
public List<Blog> selectAll() {
List<Blog> blogs = new ArrayList<Blog>();
// 1. 和数据库服务器建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "select * from blog order by blogId desc";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历结果集合.
while (resultSet.next()) {
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
String content = resultSet.getString("content");
if (content.length() > 40) {
content = content.substring(0, 40) + "...";
}
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) {
// 1. 和数据库建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "select * from blog where blogId = ?";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
statement.setInt(1, blogId);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历结果集. 要么是 0 个记录, 要么只有 1 条记录.
if (resultSet.next()) {
Blog blog = new Blog();
blog.setBlogId(resultSet.getInt("blogId"));
blog.setTitle(resultSet.getString("title"));
blog.setContent(resultSet.getString("content"));
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
return blog;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
public static void main(String[] args) {
// 通过这里的代码对上面的数据库操作进行验证.
// 1. 验证插入.
// 此处的测试, 只是一个简单的 "冒烟测试"
// Blog blog = new Blog();
// blog.setTitle("这是第三篇博客");
// blog.setContent("这是博客正文这是博客正文这是博客正文这是博客正文");
// blog.setUserId(1);
// BlogDao blogDao = new BlogDao();
// blogDao.insert(blog);
// 2. 验证查找.
// dao.BlogDao blogDao = new dao.BlogDao();
// List<dao.Blog> blogs = blogDao.selectAll();
// System.out.println(blogs);
// dao.Blog blog = blogDao.selectOne(1);
// System.out.println(blog);
// 3. 验证删除.
// dao.BlogDao blogDao = new dao.BlogDao();
// blogDao.delete(1);
}
}
UserDao:
public class UserDao {
// 注册的时候, 需要新增用户到数据库中.
// 期望 username 不能重复. (username 是用于登陆的用户名)
// 注册的时候(insert 之前), 先判定一下该 username 是否已经存在.
// 如果存在, 就直接不执行后续的 insert 操作.
// username 的唯一性不一定非得通过 数据库的约束 来完成.
// 也可以通过用户代码来完成.
public void insert(User user) {
// 1. 和数据库建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "insert into user values(null, ?, ?)";
PreparedStatement statement = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1, user.getUsername());
statement.setString(2, user.getPassword());
// 3. 执行 SQL
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
} finally {
// 4. 关闭连接.
DBUtil.close(connection, statement, null);
}
}
// 登陆的时候, 需要根据用户名获取到密码
public User selectByName(String username) {
// 1. 和数据库建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "select * from user where username = ?";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
statement.setString(1, username);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历结果集. 预期按照名字查找的结果是唯一的记录.
if (resultSet.next()) {
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException e) {
e.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
// 根据用户 id 来查用户信息
// 显示博客作者的时候需要用到.
public User selectById(int userId) {
// 1. 和数据库建立连接.
Connection connection = DBUtil.getConnection();
// 2. 拼装 SQL
String sql = "select * from user where userId = ?";
PreparedStatement statement = null;
ResultSet resultSet = null;
try {
statement = connection.prepareStatement(sql);
statement.setInt(1, userId);
// 3. 执行 SQL
resultSet = statement.executeQuery();
// 4. 遍历结果集. 预期结果只是有 0 个或者 1 个.
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 static void main(String[] args) {
UserDao userDao = new UserDao();
// 1. 测试 插入用户 功能
// dao.User user = new dao.User();
// user.setUsername("zhangsan");
// user.setPassword("123");
// userDao.insert(user);
// 2. 测试根据名字查找.
// dao.User user = userDao.selectByName("zhangsan");
// System.out.println(user);
// 3. 测试根据 id 查找.
// dao.User user = userDao.selectById(1);
// System.out.println(user);
}
}
4. 对dao层进行单元测试
BlogDaoTest:
public class BlogDaoTest {
@Test
public void insert() {
Blog blog = new Blog();
blog.setTitle("这是超级简约博客");
blog.setContent("这是博客正文这是博客正文");
blog.setUserId(1);
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
}
@Test
public void delete() {
dao.BlogDao blogDao = new dao.BlogDao();
blogDao.delete(1);
}
@Test
public void selectAll() {
dao.BlogDao blogDao = new dao.BlogDao();
List<Blog> blogs = blogDao.selectAll();
System.out.println(blogs);
}
@Test
public void selectOne() {
dao.BlogDao blogDao = new dao.BlogDao();
dao.Blog blog = blogDao.selectOne(1);
System.out.println(blog);
}
}
UserDaoTest:
public class UserDaoTest {
@Test
public void insert() {
UserDao userDao = new UserDao();
dao.User user = new dao.User();
user.setUsername("zhangsan");
user.setPassword("123");
userDao.insert(user);
}
@Test
public void selectByName() {
UserDao userDao = new UserDao();
dao.User user = userDao.selectByName("zhangsan");
System.out.println(user);
}
@Test
public void selectById() {
UserDao userDao = new UserDao();
dao.User user = userDao.selectById(1);
System.out.println(user);
}
}
4. 实现博客页面
a)博客列表页(只是展示博客内容的摘要)
b)博客详情页
c)博客编辑页
d)删除博客
e)注册
f)登录/注销
使用模板来生成html页面,使用Themeleaf模板引擎(重要)
4.1初始化Themeleaf模板引擎:
@WebListener
public class ThymeleafConfig implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// 使用这个!!!
// 这个方法就会在当前 webapp 的 ServletContext 初始化之后
// 立即执行. Thymeleaf 的初始化就在这里调用即可!!!
// 初始化 Thymeleaf
// 1) 创建一个 engine(引擎), 负责把 Java 中的数据替换到模板中.
TemplateEngine engine = new TemplateEngine();
// 2) 创建一个 resolver 对象(解析器), 负责找到 html 模板在哪, 并加载到内存中.
// 供 engine 对象来使用.
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver(sce.getServletContext());
// 3) 给 resolver 设置一些属性, 让它能够找到 html 模板.
resolver.setCharacterEncoding("utf-8");
// Prefix 表示 "前缀", 设定了满足啥样条件的文件被加载到内存中作为 HTML 模板
resolver.setPrefix("/WEB-INF/template/");
// Suffix 表示 "后缀"
resolver.setSuffix(".html");
// 4) 把 resolver 和 engine 关联起来.
engine.setTemplateResolver(resolver);
// 把初始化好的 engine 对象交给 ServletContext 来保管.
ServletContext context = sce.getServletContext();
context.setAttribute("engine", engine);
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
}
}
4.2 博客列表页的模板
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客列表页</title>
<style>
/* 清除浏览器默认样式 */
* {
margin: 0;
padding: 0;
}
body {
background-color: #f3f3f3;
}
.container {
width: 800px;
margin: 0 auto;
background-color: #fff;
}
.title {
display: block;
text-align: center;
color: #000;
font-size: 20px;
font-weight: 700;
text-decoration: none;
padding: 10px 0;
}
.post-time {
text-align: center;
color: #999aaa;
}
.desc {
padding-bottom: 10px;
text-indent: 20px;
}
.nav {
height: 50px;
background: #000;
color: #fff;
display: flex;
align-items: center;
}
.nav-title {
padding-left: 10px;
}
.nav-button {
color: #fff;
text-decoration: none;
padding: 0 10px;
}
.spacer {
width: 55%;
}
</style>
</head>
<body>
<div class="nav">
<!-- 如果是用户未登录, 就显示未登录, 并且显示登陆/注册按钮 -->
<!-- 如果是用户已登录, 就显示欢迎 XXX -->
<h3 class="nav-title">我的博客系统</h3>
<div class="spacer"></div>
<a href="login.html" class="nav-button" th:if="${!isLogin}">登陆</a>
<a href="register.html" class="nav-button" th:if="${!isLogin}">注册</a>
<div th:if="${isLogin}" th:text="${'欢迎' + user.username}"></div>
<a href="blog_insert.html" class="nav-button" th:if="${isLogin}">写博客</a>
<a href="logout" class="nav-button" th:if="${isLogin}">注销</a>
</div>
<!-- .container 存放了所有的博客 -->
<div class="container">
<!-- .blog 对应到一篇博客 -->
<div class="blog" th:each="blog : ${blogs}">
<a th:href="${'blogContent?blogId=' + blog.blogId}" class="title" th:text="${blog.title}">我是标题</a>
<div class="post-time" th:text="${blog.postTime}">2021-07-11 17:30:00</div>
<div class="desc" th:text="${blog.content}">我是正文的摘要...</div>
</div>
</div>
</body>
服务器端博客列表页的代码:
@WebServlet("/blogList")
public class BlogListServlet extends HttpServlet {
@Override
public void init() throws ServletException {
}
private User checkLogin(HttpServletRequest req) {
HttpSession session = req.getSession(false);
if (session == null) {
return null;
}
User user = (User) session.getAttribute("user");
return user;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 0. 从 req 里面读取一下当前用户信息. 判定用户是否登陆.
User user = checkLogin(req);
boolean isLogin = false;
if (user != null) {
isLogin = true;
}
// 1. 先从数据库查询出都有哪些博客.
BlogDao blogDao = new BlogDao();
List<Blog> blogList = blogDao.selectAll();
// 2. 构造博客页面.
// 1) 通过 Thymeleaf 进行渲染. 渲染的时候需要定义 "数据集合" 这样的概念.
// WebContext 功能就是把要替换的数据给收集起来, 统一的传给模板引擎.
WebContext webContext = new WebContext(req, resp, getServletContext());
// 2) setVariable 可以设置多个键值对. 完全取决于模板代码怎么写.
// 模板里的每个 ${ } 里面的内容都需要在 webContext 设定进去.
webContext.setVariable("blogs", blogList);
webContext.setVariable("isLogin", isLogin);
webContext.setVariable("user", user);
// 3) 进行渲染.
TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine");
String html = engine.process("blog_list", webContext);
System.out.println("模板渲染的内容: " + html);
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write(html);
}
}
4.3 博客详情页的模板
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客内容页</title>
<style>
/* 清除浏览器默认样式 */
* {
margin: 0;
padding: 0;
}
body {
background-color: #f3f3f3;
}
.container {
width: 800px;
margin: 0 auto;
background-color: #fff;
}
.title {
text-align: center;
padding: 10px 0;
font-size: 36px;
}
.author {
text-align: center;
padding: 10px 0;
color: #999aaa;
}
.post-time {
text-align: center;
color: #999aaa;
}
.content {
padding: 10px 0;
text-indent: 20px;
}
.nav {
height: 50px;
background: #000;
color: #fff;
display: flex;
align-items: center;
}
.nav-title {
padding-left: 10px;
}
.nav-button {
color: #fff;
text-decoration: none;
padding: 0 10px;
}
.spacer {
width: 55%;
}
</style>
</head>
<body>
<div class="nav">
<!-- 如果是用户未登录, 就显示未登录, 并且显示登陆/注册按钮 -->
<!-- 如果是用户已登录, 就显示欢迎 XXX -->
<h3 class="nav-title">我的博客系统</h3>
<div class="spacer"></div>
<a class="nav-button" th:href="${'blogDelete?blogId=' + blog.blogId}">删除</a>
</div>
<!-- 作为整个博客的容器 -->
<div class="container">
<h3 th:text="${blog.title}" class="title">这是博客标题</h3>
<div class="author" th:text="${username}">汤老湿</div>
<div class="post-time" th:text="${blog.postTime}">2021-07-11 12:00:00</div>
<div class="content" th:text="${blog.content}">
这是博客正文
这是博客正文
这是博客正文
这是博客正文
这是博客正文
</div>
</div>
</body>
服务器端博客详情页的代码:
@WebServlet("/blogContent")
public class BlogContentServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
resp.setContentType("text/html; charset=utf-8");
// 1. 从 req 中读取出 blogId
String blogId = req.getParameter("blogId");
if (blogId == null || "".equals(blogId)) {
// /rocket_blog/blogContent => blogId = null
// /rocket_blog/blogContent?blogId= blogId = ""
// resp.getWriter().write("<h3>blogId 不存在</h3>");
resp.sendError(404, "blogId 参数错误!");
return;
}
// 2. 根据 blogId 在数据库中查询出博客的详细内容.
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
if (blog == null) {
resp.sendError(404, "blogId 指定的文章不存在!");
return;
}
// 3. 根据 blog.userId 找到对应的 dao.User 对象
UserDao userDao = new UserDao();
User user = userDao.selectById(blog.getUserId());
// 4. 根据详细内容, 渲染到模板中.
TemplateEngine engine = (TemplateEngine) getServletContext().getAttribute("engine");
WebContext webContext = new WebContext(req, resp, getServletContext());
webContext.setVariable("blog", blog);
webContext.setVariable("username", user.getUsername());
String html = engine.process("blog_content", webContext);
// 5. 把渲染好的结果写回客户端.
resp.getWriter().write(html);
}
}
4.4 博客编辑页前端代码
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>博客编辑页</title>
<style>
* {
margin: 0;
padding: 0;
}
.title {
display: block;
width: 600px;
height: 50px;
margin: 10px auto;
font-size: 20px;
text-indent: 10px;
}
textarea {
display: block;
width: 600px;
margin: 10px auto;
font-size: 16px;
padding: 10px;
box-sizing: border-box;
}
.button {
display: block;
width: 200px;
height: 50px;
margin: 0 auto;
}
</style>
</head>
<body>
<form action="blogInsert" method="POST">
<!-- 用 input 表示文章的标题 -->
<input type="text" name="title" class="title">
<!-- 用这个输入框表示正文 -->
<textarea name="content" rows="30"></textarea>
<input type="submit" value="发布博客" class="button">
</form>
</body>
服务器端博客编辑页代码:
@WebServlet("/blogInsert")
public class BlogInsertServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
// 0. 判定用户是否已经登陆
HttpSession session = req.getSession(false);
if (session == null) {
resp.sendError(404, "当前尚未登陆, 不能发布博客!");
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
resp.sendError(404, "当前尚未登陆, 不能发布博客!");
return;
}
// 1. 读取请求中的参数.
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || "".equals(title)
|| content == null || "".equals(content)) {
resp.sendError(404, "标题或者正文为空");
return;
}
// 2. 根据读到的数据构造 dao.Blog 对象, 并插入数据库
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
// 3. 重定向到博客列表页, 也能直接看到新的博客
resp.sendRedirect("blogList");
}
}
4.5 服务器端删除博客代码:(删除本身在博客详情页当中体现)
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 从 req 中读取要删除的 blogId
String blogId = req.getParameter("blogId");
if (blogId == null || "".equals(blogId)) {
resp.sendError(404, "blogId 为空!");
return;
}
// 2. 判定用户的登陆状态, 未登陆不能删除.
HttpSession session = req.getSession(false);
if (session == null) {
resp.sendError(404, "当前未登录, 不能删除!");
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
resp.sendError(404, "当前未登录, 不能删除!");
return;
}
// 3. 根据 blogId, 查询 blogId 的作者 id, 看看和当前登陆的用户 id 是否相同
// 不相同也不能删除.
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
if (blog == null) {
resp.sendError(404, "当前博客不存在!");
return;
}
if (blog.getUserId() != user.getUserId()) {
resp.sendError(403, "不能删除别人的博客!");
return;
}
// 4. 如果用户相同, 从数据库中删除这个博客即可.
blogDao.delete(Integer.parseInt(blogId));
// 5. 重定向到博客列表页
resp.sendRedirect("blogList");
}
}
4.6 登录页面前端代码:(登录本身在博客列表页当中体现)
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>登陆页面</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
.container {
width: 800px;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
flex-direction: column;
}
input {
display: block;
margin-bottom: 10px;
width: 315px;
height: 35px;
font-size: 20px;
text-indent: 10px;
}
.button {
color: #fff;
background-color: orange;
border: none;
border-radius: 5px;
}
h3 {
padding: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h3>请登录博客系统</h3>
<form action="login" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交" class="button">
</form>
</div>
</body>
服务器端登录页面代码:
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 从 req 读取用户提交的 username 和 password
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null || "".equals(username)
|| password == null || "".equals(password)) {
resp.sendError(404, "用户名或密码不能为空");
return;
}
// 2. 从数据库中查找指定用户名的用户信息.
UserDao userDao = new UserDao();
User user = userDao.selectByName(username);
if (user == null) {
resp.sendError(404, "用户名或者密码错误");
return;
}
if (!password.equals(user.getPassword())) {
// 密码不匹配
resp.sendError(404, "用户名或者密码错误");
return;
}
// 3. 登陆成功! 创建会话.
HttpSession session = req.getSession(true);
session.setAttribute("user", user);
// 4. 直接把用户页面重定向到博客列表页.
resp.sendRedirect("blogList");
}
}
4.7 服务器端注销业务代码:(注销本身在博客列表页当中体现)
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 把当前用户的 session 中的 user 这个数据给删掉.
HttpSession session = req.getSession(false);
if (session == null) {
resp.sendError(404, "当前尚未登陆, 不能注销");
return;
}
session.removeAttribute("user");
resp.sendRedirect("blogList");
}
}
4.8 注册页面前端代码:(注册本身在博客列表页当中体现)
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>注册页面</title>
<style>
* {
margin: 0;
padding: 0;
}
html,
body {
height: 100%;
}
.container {
width: 800px;
margin: 0 auto;
display: flex;
justify-content: center;
align-items: center;
height: 100%;
flex-direction: column;
}
input {
display: block;
margin-bottom: 10px;
width: 315px;
height: 35px;
font-size: 20px;
text-indent: 10px;
}
.button {
color: #fff;
background-color: orange;
border: none;
border-radius: 5px;
}
h3 {
padding: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h3>请注册博客系统</h3>
<form action="register" method="POST">
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="注册" class="button">
</form>
</div>
</body>
服务器端注册业务代码:
@WebServlet("/register")
public class RegisterServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 如果不做任何限定, 此时服务器这边读取 Parameter 的时候默认不是按照 utf-8 的方式来理解字符编码的.
req.setCharacterEncoding("utf-8");
// 1. 先读取用户提交的用户名和密码
String username = req.getParameter("username");
String password = req.getParameter("password");
if (username == null || "".equals(username)
|| password == null || "".equals(password)) {
resp.sendError(404, "提交的用户名或者密码为空");
return;
}
// 2. 查询数据库, 看看 username 是否存在
UserDao userDao = new UserDao();
User existsUser = userDao.selectByName(username);
if (existsUser != null) {
// 用户已经存在! 提示注册失败!
resp.sendError(404, "用户名已经存在, 注册失败!");
return;
}
// 3. 构造 dao.User 对象, 插入到数据库中.
User newUser = new User();
newUser.setUsername(username);
newUser.setPassword(password);
userDao.insert(newUser);
// 4. 返回一个结果.
resp.setContentType("text/html; charset=utf-8");
resp.getWriter().write("<h3>注册成功!</h3>");
}
}
5. 项目功能展示
(1)博客列表页:
有列表内博客题目,发布时间,博客摘要等内容
点击文章标题即可进入博客详情页
有登录和注册功能
(2)登录页面
1. 未输入就登录时返回404
2. 使用未注册的用户登录时返回404
3. 登录成功后跳转到博客列表页:
有写博客和注销功能
(3)注册页面
1. 未输入就注册返回404
2. 输入已存在的用户名返回404
3. 注册成功返回字段提示
(4)注销功能
在登陆成功状态点击注销即可回到初始博客列表页
(5)博客详情页
有博客的题目,作者的用户名,发布时间,博客正文等内容
点击删除即可删除此文章
(6)博客编辑页
1. 在登陆成功状态点击写博客即可跳转到博客编辑页
2. 编辑一些内容点击发布博客就可以成功写入博客列表页
3. 点击发布博客即可跳转到博客列表页,此时发布的博客就会出现在最上方
(7)博客删除功能
在博客详情页点击删除功能就可以实现删除
1. 每一篇博客都有userId,每个用户只能删除自己发布的博客,例如登录的是duyuhang用户,就不能删除上图zhangsan用户的博客
2. 登录的是duyuhang用户,删除下图duyuhang发布的博客
删除成功后跳转到博客列表页
6. 总结
这个项目是我的第一个项目,项目本身并不难,但是代码量还是有点分量的
如下是我遇到的一些问题,给大家参考一下(其中不乏有无语且无聊的问题,汗...)
错误整理:
1. DBUtil中password应该和自己的mysql一致
2. 博客详情页blogContent出现500,出现空指针异常,原因是我的user为null,数据库中user表没有数据
3. login.html出现404,原因是login.html应该写在webapp中,我写到了WEB-INF中导致找不到
4. if();{},小括号后多加了一个;
5. 在返回值是void的方法中,return会结束方法,不执行return后面的语句
6. 博客注册页面中form表单的action填成login出错,应和registerServlet一致
7. 模板blog_content的 th:href="${'blogDelete?blogId=' + blog.blogId}"最后的引号前面少了一个花括号
改进的内容:
1. 注册页面注册成功后延时3s跳转到登录页面
7. 个人心得体会
写的过程也是磕磕绊绊,从7月17日16:00~22:30
7月18日18:30~22:30
7月19日10:30~12:40---1:30~4:00
总共分了4次才写完,万事开头难,第一次做项目感觉收获还是非常多的,
觉得获得了飞升的感觉~~
项目练习一遍是不够的,总结好,有时间在写一遍写到完全熟悉掌握才行
这次挑选了一张非常有意义的图片,对IG来说是,对我来说也是
最后美图收尾嘻嘻~~ (IG 2018 我们是冠军)