效果图:
登录页:
博客列表页:
博客详情页:
博客编辑页:
参考博客:https://blog.csdn.net/yyhgo_/article/details/128468738?spm=1001.2014.3001.5501
按步骤创建出 Maven 项目并引入依赖、创建目录!
(引入 Servlet 依赖、Jackson 依赖、mysql 依赖)
pom.xml:
<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.0modelVersion>
<groupId>org.examplegroupId>
<artifactId>blog_systemartifactId>
<version>1.0-SNAPSHOTversion>
<properties>
<maven.compiler.source>8maven.compiler.source>
<maven.compiler.target>8maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>javax.servletgroupId>
<artifactId>javax.servlet-apiartifactId>
<version>3.1.0version>
<scope>providedscope>
dependency>
<dependency>
<groupId>com.fasterxml.jackson.coregroupId>
<artifactId>jackson-databindartifactId>
<version>2.13.4version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.49version>
dependency>
dependencies>
project>
本系统要存入博客文章的信息和用户的信息
1)创建博客表:
博客的 id,博客的标题,博客的内容,博客的日期,博文的博主 id
2)创建用户表:
用户 id 用户名 用户密码
在 main 目录下创建文件 db.sql:
create database if not exists yyhgo charset utf8mb4;
use yyhgo;
drop table if exists blog;
create table blog (
blogId int primary key auto_increment,
title varchar(1024), -- 博客标题
content mediumtext, -- 博客正文
userId int, -- 作者的 id
postTime datetime -- 发布时间
);
drop table if exists user;
create table user (
userId int primary key auto_increment,
username varchar(128) unique,
password varchar(128)
);
insert into blog values(null, "这是第一篇博客", "从今天开始, 我要认真写代码", 1, '2022-11-24 20:00:00');
insert into blog values(null, "这是第二篇博客", "从今天开始, 我要认真写代码", 1, '2022-11-25 20:00:00');
insert into blog values(null, "这是第三篇博客", "从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码从今天开始, 我要认真写代码", 1, '2022-11-26 20:00:00');
insert into user values(null, "张三", "123");
insert into user values(null, "李四", "12345");
同时在数据库中复制粘贴这段代码。
建议把 sql 写到一个文件中。后续如果需要把数据库往别的主机上部署,就可以直接复制粘贴建库建表语句完成 ~~
把一会需要用到的数据库操作的 jdbc 代码封装起来,以备后用 ~~
创建包 model 用来存放数据库的代码!
用于创建数据源、建立网络连接、释放资源。
package model;
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;
// 通过这个类, 来封装数据库建立连接/断开连接操作
// 懒汉模式要考虑线程安全问题~~
// Servlet 程序天然就是运行在多线程环境中的. 每个请求都可能对应着一个线程(Tomcat 是通过多线程的方式来处理很多请求)
public class DBUtil {
private volatile static DataSource dataSource = null;
private static DataSource getDataSource() {
if (dataSource == null) {
synchronized (DBUtil.class) {
if (dataSource == null) {
dataSource = new MysqlDataSource();
((MysqlDataSource)dataSource).setURL("jdbc:mysql://127.0.0.1:3306/yyhgo?characterEncoding=utf8&useSSL=false");
((MysqlDataSource)dataSource).setUser("root");
((MysqlDataSource)dataSource).setPassword("");
}
}
}
return dataSource;
}
public static Connection getConnection() throws SQLException {
return getDataSource().getConnection();
}
public static void close(Connection connection, PreparedStatement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
} catch (SQLException 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();
}
}
}
}
是实体类,代表一篇博客。
实体类:对应数据库表里的一条记录 ~~
package model;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
// 这个类的对象, 表示一篇博客
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;
}
// 把这个方法魔改一下!! 在方法里面把时间戳构造成格式化时间. 以 String 的方式来返回.
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;
}
}
是实体类,代表一个用户。
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;
}
}
主要是针对博客表进行增删改查操作。
Dao:Data Access Object
用来访问数据的类。这是习惯命名方式 ~
涉及到大量的 jdbc 操作,参考博客:https://blog.csdn.net/yyhgo_/article/details/128061324?spm=1001.2014.3001.5501
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;
// 针对博客要实现的功能:
// 1. 新增博客 (博客编辑页)
// 2. 查询出博客列表 (博客列表页)
// 3. 查询出指定博客的详情 (博客详情页)
// 4. 删除指定的博客 (可以在博客详情页中加入)
public class BlogDao {
// 下列代码都是 JDBC 操作. 代码相似性非常高的!!
// 此处的 Blog 对象是前端提交给后端的.
public void insert(Blog blog) {
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. 执行 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 {
// 1. 和数据库建立连接
connection = DBUtil.getConnection();
// 2. 构造 SQL
// 排序:新插入的博客在上面!
String sql = "select * from blog order by postTime desc";
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");
// 只截取前 100 个字符作为摘要. 注意! 此处的 100 是拍脑门的!!
// 具体设置成几~~ 没关系, 只要最终的效果正确好看即可!
if (content.length() > 100) {
content = content.substring(0, 100);
}
blog.setContent(content);
blog.setUserId(resultSet.getInt("userId"));
blog.setPostTime(resultSet.getTimestamp("postTime"));
blogs.add(blog);
}
} catch (SQLException throwables) {
throwables.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;
try {
connection = DBUtil.getConnection();
String sql = "delete from blog where blogId = ?";
statement = connection.prepareStatement(sql);
statement.setInt(1, blogId);
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;
// 关于 User 表, 涉及到的操作
// 1. 根据用户名来查询用户信息(实现登录的时候)
// 2. 根据用户的 id 来查询用户信息 (获取文章的时候, 根据博客的 userId 拿到作者的信息)
public class UserDao {
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();
if (resultSet.next()) {
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
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();
if (resultSet.next()) {
User user = new User();
user.setUserId(resultSet.getInt("userId"));
user.setUsername(resultSet.getString("username"));
user.setPassword(resultSet.getString("password"));
return user;
}
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, statement, resultSet);
}
return null;
}
}
只要把前面的静态页面拷贝到项目的 webapp 目录中,就可以启动 tomcat 通过浏览器访问了 ~~
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
BlogDao blogDao = new BlogDao();
List<Blog> blogs = blogDao.selectAll();
String jsonString = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(jsonString);
}
}
在 blog_list.html 中 实现 ajax
注意:引入依赖 (jquery)
参考代码:
<div class="container-right">
div>
实现:
<script src="https://code.jquery.com/jquery-3.6.1.min.js">script>
<script src="js/app.js">script>
<script>
// 通过这个函数, 来从服务器获取到博客列表的数据
function getBlogs() {
$.ajax({
type: 'get',
url: 'blog',
success: function(body) {
// 根据返回的 json 数据, 来构造出页面内容, div.blog
// jquery ajax 会自动的把响应得到的 body 按照响应的 Content-Type 进行转换格式.
// 如果响应的 Content-Type 是 json, 此时就会自动把 body 转成 js 的对象
let container = document.querySelector('.container-right');
for (let blog of body) {
let blogDiv = document.createElement('div');
blogDiv.className = 'blog';
// 创建博客标题
let titleDiv = document.createElement('div');
titleDiv.className = 'title';
titleDiv.innerHTML = blog.title;
blogDiv.appendChild(titleDiv);
// 创建日期
let dateDiv = document.createElement('div');
dateDiv.className = 'date';
dateDiv.innerHTML = blog.postTime;
blogDiv.appendChild(dateDiv);
// 创建摘要
let descDiv = document.createElement('div');
descDiv.className = 'desc';
descDiv.innerHTML = blog.content;
blogDiv.appendChild(descDiv);
// 创建查看全文按钮
let a = document.createElement('a');
a.innerHTML = '查看全文 >>';
a.href = 'blog_detail.html?blogId=' + blog.blogId;
blogDiv.appendChild(a);
// 把 blogDiv 加入外层元素
container.appendChild(blogDiv);
}
}
});
}
// 获取博客列表
getBlogs();
script>
点击"查看全文",先让页面跳转到博客详情页 (blog_detail.html)。跳转的过程中,给 URL 带上当前要获取的博客 id。在 blog_detail.html 页面中,再通过 ajax 从服务器获取博客详情内容 ~~
约定请求的路径是 /blog,代码中当前已经有了一个 /blog 的 Servlet 了,就在之前的 Servlet 基础上做出修改即可!
基于同一个 Servlet,同一个 doGet 方法,让它既可以处理获取博客列表,又能获取博客详情:
- 获取博客列表,请求是 /blog
- 获取博客详情,请求是 /blog?blogld=1
在 doGet 里面就可以根据这个参数 是否存在 来决定 是返回博客列表还是博客详情!!!
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
String blogId = req.getParameter("blogId");
BlogDao blogDao = new BlogDao();
if (blogId == null) {
// 不存在 blogId 这个参数, 这就是获取博客列表.
List<Blog> blogs = blogDao.selectAll();
String jsonString = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(jsonString);
} else {
// 存在 blogId 参数, 就是获取博客详情.
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
String jsonString = objectMapper.writeValueAsString(blog);
resp.getWriter().write(jsonString);
}
}
}
参考代码:
<div class="container-right">
<div class="blog-content">
<h3>我的第一篇博客h3>
<div class="date">2022-09-21 12:00:00div>
<div id="content" style="opacity: 80%">
div>
div>
div>
实现:
<script src="https://code.jquery.com/jquery-3.6.1.min.js">script>
<script src="js/app.js">script>
<script>
function getBlog() {
$.ajax({
type: 'get',
url: 'blog' + location.search,
success: function(body) {
// body 就是得到的一个 json 格式的 博客数据. 由于响应的 Content-Type 是 application/json
// 因此 jquery 就会自动把响应数据转成 js 对象.
let h3 = document.querySelector('.blog-content h3');
h3.innerHTML = body.title;
let divDate = document.querySelector('.blog-content .date');
divDate.innerHTML = body.postTime;
// 刚才这里是直接把正文内容设置到 innerHTML 中了, 没有渲染的过程的.
// let divContent = document.querySelector('#content');
// divContent.innerHTML = body.content;
// 靠谱的做法, 应该是先使用 editor.md 进行渲染.
// [重要] 这个方法就是 editor.md 提供的一个方法把 markdown 字符串转成格式化的效果.
// 第一个参数是一个 div 的 id, 表示要把渲染结果放到哪个 div 中.
// 第二个参数是一个 js 对象, 把正文内容传入即可. (还支持很多别的参数属性, 此处暂时不涉及)
editormd.markdownToHTML('content', {
markdown: body.content
});
}
});
}
// 在页面加载之后, 要调用代码.
getBlog();
script>
细节:
1)url: 'blog' + location.search,
在 js 中,可以通过这样的方式得到当前页面的 query string,即 ?blogId=1
(location 是 js 中特殊的全局变量)
2)运行后在页面控制台会有这样的警告:
这里的 404 没事 ~ (favicon 是页面的图标,当前没这个图标就报错了)
3)博客编辑页是一个 markdown 格式的数据。就希望当获取到博客详情的时候,也能按照 markdown 来渲染!
1.在页面中引入 editor.md:
2.通过 editormd.markdownToHTML('content', { markdown: body.content });
渲染。
(关注代码注释)
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected 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");
if (username == null || password == null || username.equals("") || password.equals("")) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您当前的用户名或者密码为空!");
return;
}
// 加上一个打印, 看看服务器读取的用户名密码到底是什么鬼!!
System.out.println("username=" + username + ", password=" + password);
// 2. 查询数据库, 看密码是否匹配
UserDao userDao = new UserDao();
User user = userDao.selectByName(username);
if (user == null) {
// 用户不存在.
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您的用户名或者密码错误!");
return;
}
if (!user.getPassword().equals(password)) {
// 密码错误!
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您的用户名或者密码错误!");
return;
}
// 3. 登录成功之后, 构造会话.
HttpSession session = req.getSession(true);
// 把刚才获取到的 user 对象给存到 session 里, 方便后续使用.
session.setAttribute("user", user);
// 4. 返回一个重定向报文, 跳转到博客列表页.
resp.sendRedirect("blog_list.html");
}
form 表单是可以搭配 302 进行跳转的!
如果是使用 ajax,其响应是不能处理 302,不会有跳转的 (需要使用别的方法来跳转)!
修改代码,放到 form 表单里面去!!!
<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" 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>
在博客列表页 / 详情页里,访问页面的时候验证用户的登录状态。
我们登录之后,服务器会在内存中保存 session 对象 (维护了用户的信息)
一旦服务器重启,此时内存中的数据就没了,自然登录状态就丢失了!
当用户进入博客列表 / 博客详情页的时候,先发起一个单独的 ajax 请求,通过这个请求来验证用户登录状态!
服务器这边根据登录 / 未登录,返回不同的结果。客户端就可以根据返回的响应,来决定是否要强制跳转到登录页!!!
一个页面是可以发送多个 ajax 的,想发几个都行 ~~
在原有 LoginServlet 的基础上添加代码!
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected 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");
if (username == null || password == null || username.equals("") || password.equals("")) {
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您当前的用户名或者密码为空!");
return;
}
// 加上一个打印, 看看服务器读取的用户名密码到底是什么鬼!!
System.out.println("username=" + username + ", password=" + password);
// 2. 查询数据库, 看密码是否匹配
UserDao userDao = new UserDao();
User user = userDao.selectByName(username);
if (user == null) {
// 用户不存在.
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您的用户名或者密码错误!");
return;
}
if (!user.getPassword().equals(password)) {
// 密码错误!
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("您的用户名或者密码错误!");
return;
}
// 3. 登录成功之后, 构造会话.
HttpSession session = req.getSession(true);
// 把刚才获取到的 user 对象给存到 session 里, 方便后续使用.
session.setAttribute("user", user);
// 4. 返回一个重定向报文, 跳转到博客列表页.
resp.sendRedirect("blog_list.html");
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 验证登录状态.
// 直接去取登录状态. 看能不能取到.
HttpSession session = req.getSession(false);
if (session == null) {
// 未登录, 直接设置状态码 403 即可. body 都不需要~
resp.setStatus(403);
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
// 未登录, 直接设置状态码 403 即可. body 都不需要~
resp.setStatus(403);
return;
}
// 已登录!
resp.setStatus(200);
}
}
function checkLogin() {
$.ajax({
type: 'get',
url: 'login',
success: function (body) {
// 200, 登录成功, 不必做任何处理.
},
error: function () {
// 403 就会触发 error
// 强行跳转到登录页面.
location.assign('login.html');
}
});
}
location.assign()
是前端代码实现页面跳转的方式!!!
博客列表页和详情页都需要去完成这样一件事,所以把代码写进 app.js 文件,放进 js 目录里 ~~
在 blog_list.html 和 blog_detail.html 文件中引入 app.js:
<script src="js/app.js">script>
并且调用函数!:
checkLogin();
ajax 是异步的:发起请求的主体,不负责接受结果,而是由别人主动推送过来!
所以多个 ajax 之间执行间隔极短,就可以近似看作是同时发送!
所以不必关注多个 ajax 之间的先后顺序!!!
在博客列表页,和博客详情页都有用户信息。
这里的信息不要写死,而是能够从服务器动态获取!
图片的话一般是保存在一个单独的位置,在数据库里存图片的路径 ~~
@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String blogId = req.getParameter("blogId");
if (blogId == null) {
// 请求来自博客列表页, 直接返回登录的用户信息.
HttpSession session = req.getSession(false);
if (session == null) {
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;
}
user.setPassword("");
resp.setContentType("application/json; charset=utf8");
String jsonString = objectMapper.writeValueAsString(user);
resp.getWriter().write(jsonString);
} else {
// 请求来自博客详情页, 返回文章作者信息.
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("当前 blogId 有误!");
return;
}
UserDao userDao = new UserDao();
User author = userDao.selectById(blog.getUserId());
if (author == null) {
resp.setStatus(403);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前博客对应的作者没有找到!");
return;
}
author.setPassword(""); // 为了安全,隐藏密码~~
resp.setContentType("application/json; charset=utf8");
String jsonString = objectMapper.writeValueAsString(author);
resp.getWriter().write(jsonString);
}
}
}
参考代码:
<div class="container-left">
<div class="card">
<img src="image/doge.jpg" alt="">
<h3>小豪h3>
<a href="#">github 地址a>
<div class="counter">
<span>文章span>
<span>分类span>
div>
<div class="counter">
<span>2span>
<span>3span>
div>
div>
div>
实现:
<script>
// 获取当前用户的信息
function getUserInfo() {
$.ajax({
type: 'get',
url: 'userInfo',
success: function (body) {
// 让后端在查询失败的时候, 不要返回 200 , 而是返回 403 .
// 避免在前端触发 success 分支.
let h3 = document.querySelector('.card h3');
h3.innerHTML = body.username;
}
});
}
getUserInfo();
script>
实现:
<script>
// 获取用户信息
function getUserInfo() {
$.ajax({
type: 'get',
url: 'userInfo' + location.search,
success: function (body) {
// 让后端在查询失败的时候, 不要返回 200 , 而是返回 403 .
// 避免在前端触发 success 分支.
let h3 = document.querySelector('.card h3');
h3.innerHTML = body.username;
}
});
}
getUserInfo();
script>
这个功能要做的工作:
此处的注销就是退出登录。
点击注销的时候,发送一个 GET 请求,并且跳转到博客登录页。
直接借助 a 标签 来实现!
a 标签 点击之后正好是发送了一个 GET 请求,也正好能触发页面跳转 ~~
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 注销要做的是删除用户的会话信息. 因此就得先确认用户有没有会话.
// req 对象没有直接提供一个 删除会话 的操作~~
// 删除会话有个办法, 就是把过期时间设置成 0. 比较麻烦.
// 更简单的办法, 虽然保留会话对象, 但是把会话里的 user 给删了.
HttpSession session = req.getSession(false);
if (session == null) {
// resp.setStatus(403);
resp.sendRedirect("login.html");
return;
}
session.removeAttribute("user");
resp.sendRedirect("login.html");
}
}
注销要做的是删除用户的会话信息,因此先确认用户有没有会话。
req 对象没有直接提供一个 删除会话 的操作!删除会话有个办法,就是把过期时间设置成 0,但比较麻烦。
更简单的办法:虽然保留会话对象,但是把会话里的 user 给删了!
直接通过 a 标签 来进行实现,不需要加上任何的 ajax 请求!
<div class="nav">
<img src="image/logo2.jpg" alt="">
<span class="title">我的博客系统span>
<span class="spacer">span>
<a href="blog_list.html">主页a>
<a href="blog_edit.html">写博客a>
<a href="logout">注销a>
div>
博客列表页、详情页、编辑页都进行修改!!!
把请求中的博客数据拿到,同时写入数据库!
前面实现博客列表界面时,已经使用过 blog 路径,有了 BlogServlet 这个类 (doGet 方法)。
在这个类里添加 doPost 方法即可 ~~
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
String blogId = req.getParameter("blogId");
BlogDao blogDao = new BlogDao();
if (blogId == null) {
// 不存在 blogId 这个参数, 这就是获取博客列表.
List<Blog> blogs = blogDao.selectAll();
String jsonString = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(jsonString);
} else {
// 存在 blogId 参数, 就是获取博客详情.
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
String jsonString = objectMapper.writeValueAsString(blog);
resp.getWriter().write(jsonString);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf8");
// 1. 获取到用户的登录状态.
HttpSession session = req.getSession(false);
if (session == null) {
resp.setStatus(403);
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
resp.setStatus(403);
return;
}
// 2. 读取请求的内容
String title = req.getParameter("title");
String content = req.getParameter("content");
if (title == null || title.equals("") || content == null || content.equals("")) {
resp.setStatus(400);
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("请求中的标题或正文不完整");
return;
}
// 3. 构造 Blog 对象, 并插入到数据库中.
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
// 博客的作者. 作者是谁? 当前谁登录, 作者就是谁!!
blog.setUserId(user.getUserId());
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
// 4. 插入成功之后, 跳转到博客列表页.
resp.sendRedirect("blog_list.html");
}
}
这个地方可以使用 ajax,也可以使用 form 表单。
此处就使用 form 表单 即可 ~~
<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 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>
有时页面会触发缓存,这时候要强制刷新!Ctrl + F5!
记得修改 form 元素的属性 (设置高度什么的 ~)
删除按钮可以放在列表页,也可以放在详情页 ~~
这里我们放在详情页。
所以此处要先根据当前用户的状态,来判断是否显示删除按钮!!!
需要修改一下 UserInfoServlet 的 doGet 方法,来判断一下登录用户与博客作者 (从数据库中查) 的关系!
@WebServlet("/userInfo")
public class UserInfoServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String blogId = req.getParameter("blogId");
// 先获取一下当前是哪个用户登录的
HttpSession session = req.getSession(false);
if (session == null) {
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) {
// 请求来自博客列表页, 直接返回登录的用户信息.
user.setPassword("");
resp.setContentType("application/json; charset=utf8");
String jsonString = objectMapper.writeValueAsString(user);
resp.getWriter().write(jsonString);
} else {
// 请求来自博客详情页, 返回文章作者信息.
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("当前 blogId 有误!");
return;
}
UserDao userDao = new UserDao();
// author 是博客的作者
User author = userDao.selectById(blog.getUserId());
if (author == null) {
resp.setStatus(403);
resp.setContentType("text/html;charset=utf8");
resp.getWriter().write("当前博客对应的作者没有找到!");
return;
}
author.setPassword(""); // 为了安全,隐藏密码~~
if (user.getUserId() == author.getUserId()) {
author.setIsYourBlog(1);
} else {
author.setIsYourBlog(0);
}
resp.setContentType("application/json; charset=utf8");
String jsonString = objectMapper.writeValueAsString(author);
resp.getWriter().write(jsonString);
}
}
}
User 类中有一个 isYourBlog 属性 ~~
(使用 int 类型!若使用 boolean 类型可能会与属性名中的 “is” 产生一些冲突,会出问题)
参考代码:
<div class="nav">
<img src="image/logo2.jpg" alt="">
<span class="title">我的博客系统span>
<span class="spacer">span>
<a href="blog_list.html">主页a>
<a href="blog_edit.html">写博客a>
<a href="logout">注销a>
div>
实现:
<script>
// 获取用户信息
function getUserInfo() {
$.ajax({
type: 'get',
url: 'userInfo' + location.search,
success: function (body) {
// 让后端在查询失败的时候, 不要返回 200 , 而是返回 403 .
// 避免在前端触发 success 分支.
let h3 = document.querySelector('.card h3');
h3.innerHTML = body.username;
if (body.isYourBlog) {
// 在导航栏中加个按钮, 用来删除文章.
let deleteA = document.createElement('a');
// location.search 就是当前页面 url 的 query string, 也就是
// ?blogId=1 这样的结果.
deleteA.href = 'blogDelete' + location.search;
deleteA.innerHTML = '删除';
let navDiv = document.querySelector('.nav');
navDiv.appendChild(deleteA);
}
}
});
}
getUserInfo();
script>
JavaScript 是弱类型。number 和 boolean 是可以相互转换的!不像 Java 要求的严格 ~
因此使用if (body.isYourBlog) {
判断完全可以,因为 非0 就为 true !!!
实现具体的删除动作。
前端代码已经完成了,成功运行后会添加一个 a 标签 (删除)!
点击 “删除” 按钮,即发送一个 GET 请求 (a 标签跳转) ~~
处理这个请求!!!
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 先判定用户的登录状态, 如果用户未登录, 就不给删除
HttpSession session = req.getSession(false);
if (session == null) {
resp.setStatus(403);
return;
}
User user = (User) session.getAttribute("user");
if (user == null) {
resp.setStatus(403);
return;
}
// 2. 获取到 blogId
String blogId = req.getParameter("blogId");
if (blogId == null || blogId.equals("")) {
resp.setContentType("text/html; charset=utf8");
resp.getWriter().write("要删除的文章不存在!!");
return;
}
// 3. 删除数据库中的数据
BlogDao blogDao = new BlogDao();
blogDao.delete(Integer.parseInt(blogId));
// 4. 跳转到博客列表页
resp.sendRedirect("blog_list.html");
}
}