项目类型:Java Web项目
主要技术:Html、CSS、JS、Servlet、MySQL
开发工具:IDEA
数据库:MySQL
数据库表:2张
项目介绍:使用约定前后端接口,前后端分离的方法。后端采用Servlet,前端使用的是Html、CSS、JS。基本的功能包括用户登录、显示用户信息 、博客的编辑与发布、删除博客和用户注销功能。判定用户状态限制用户可操作的功能。
页面展示
1.创建maven项目
1.创建maven项目并创建好所需要的webapp目录
2.在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>
3.引入依赖(pom.xml)
<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/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.6.1</version>
</dependency>
<!-- 引入 mysql 驱动包 -->
<!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
</dependencies>
<packaging>war</packaging>
<build>
<finalName>blog_sys</finalName>
</build>
2.导入写好的前端代码
将之前写好的前端代码导入到webapp目录下
3.设计数据库
本系统的两个实体信息包括用户和博客,因此创建用户表和博客表。
创建用户表:
用户id ,用户名,用户密码
创建博客表:
博客的 id,博客的标题,博客的内容,博客的日期,作者 id
db.sql
-- 编写建库建表的 sql
create database if not exists blog_sys;
use blog_sys;
-- 创建一个博客表
drop table if exists blog;
create table blog (
blogId int primary key auto_increment,
title varchar(1024),
content mediumtext,
userId int,
postTime datetime
);
-- 给博客表插入数据,方便测试
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, '这是第一篇博客', '从今天开始,我要认真学java', 2, now());
insert into blog values(null, '这是第二篇博客', '从今天开始,我要认真学java', 2, now());
insert into blog values(null, '这是第三篇博客', '从今天开始,我要认真学java', 2, now());
insert into blog values(null, '这是第四篇博客', '从今天开始,我要认真学java', 2, now());
-- 创建一个用户表
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');
4.封装数据库操作
创建model包用来存放数据库操作代码
1)创建 DBUtil 类
用于和数据库建立连接
public class DBUtil {
private static final String URL = "jdbc:mysql://127.0.0.1:3306/blog_sys?characterEncoding=utf8&useSSL=false";
private static final String USERNAME = "root";
private static final String PASSWORD = "";
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 static void close(Connection connection, PreparedStatement statement, ResultSet resultSet){
if(resultSet != null){
try {
resultSet.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(statement != null){
try {
statement.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(connection != null){
try {
connection.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
2)创建 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;
}
//把这里的getter方法给改了,不是返回一个时间戳对象,而是返回一个String(格式化好的时间)
public String getPostTime() {
//使用SimpleDateFormat来完成时间戳到格式化日期时间的转换
//这个转换过程,需要在构造方法中指定要转换的格式,然后调用format来进行转换
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return simpleDateFormat.format(postTime);
}
public void setPostTime(Timestamp postTime) {
this.postTime = postTime;
}
}
3)创建 User
用户信息和获取、设置用户信息方法的抽象类
public class User {
private int userId = 0;
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;
}
}
4 )创建BlogDao类
对博客增删查改的操作的抽象类
实现操作包括:
- 往博客表里插入一个博客
- 能够获取到博客表中的所有博客信息(用在博客列表页,此处每篇博客不一定会获取到完整的正文
- 能够根据博客id获取到指定的博客内容(用在博客详情页)
- 从博客列表中根据博客id删除博客
//这个类用于封装博客表的基本操作
public class BlogDao {
//1.往博客表里插入一个博客
public void insert(Blog blog) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
//1)和数据库建立连接
connection = DBUtil.getConnection();
//2)构造SQL语句
String sql = "insert into blog values(null, ?, ?, ?, now())";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, blog.getTitle());
preparedStatement.setString(2, blog.getContent());
preparedStatement.setInt(3, blog.getUserId());
//3.执行sql
preparedStatement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
//4.关闭连接
DBUtil.close(connection, preparedStatement, null);
}
}
//2.能够获取到博客表中的所有博客信息(用在博客列表页,此处每篇博客不一定会获取到完整的正文
public List<Blog> selectAll(){
List<Blog> blogs = new ArrayList<Blog>();
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from blog order by postTime desc";
preparedStatement = connection.prepareStatement(sql);
resultSet = preparedStatement.executeQuery();
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() > 50){
content = content.substring(0, 50) + "...";
}
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, preparedStatement, resultSet);
}
return blogs;
}
//3.能够根据博客id获取到指定的博客内容(用在博客详情页)
public Blog selectOne(int blogId){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from blog where blogId = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, blogId);
resultSet = preparedStatement.executeQuery();
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 throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, preparedStatement, resultSet);
}
return null;
}
//4.从博客列表中根据博客id删除博客
public void delete(int blogId){
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
connection = DBUtil.getConnection();
String sql = "delete from blog where blogId = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, blogId);
preparedStatement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
} finally {
DBUtil.close(connection, preparedStatement, null);
}
}
}
5)创建UserDao类
对用户信息的查找操作的抽象类
实现操作包括:
- 根据用户名里查找用户信息,会在登录逻辑中使用
- 根据用户id来找用户信息,博客详情也可以根据用户id来查询作者的名字,把作者的名字显示出来
//针对用户表的基本操作
public class UserDao {
//1.根据用户名里查找用户信息,会在登录逻辑中使用
public User selectByName(String username){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from user where username = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setString(1, username);
resultSet = preparedStatement.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, preparedStatement, resultSet);
}
return null;
}
//2.根据用户id来找用户信息,博客详情也可以根据用户id来查询作者的名字,把作者的名字显示出来
public User selectById(int userId){
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
try {
connection = DBUtil.getConnection();
String sql = "select * from user where userId = ?";
preparedStatement = connection.prepareStatement(sql);
preparedStatement.setInt(1, userId);
resultSet = preparedStatement.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, preparedStatement, resultSet);
}
return null;
}
}
5.实现博客主界面
创建controller包用来存放页面逻辑代码
1)约定前后端交互接口
这里的content与其说是“正文”不如说是正文摘要,博客列表页中主要显示有哪些博客,因此就需要在这个地方吧征文进行截取。
2)编写服务器代码
通过这个类继承HttpServlet,来处理/blog路径对应的请求
@WebServlet("/blog")
public class BlogServlet extends HttpServlet {}
重写doGet方法,这个方法用来获取到数据库的博客列表
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json; charset=utf-8");
BlogDao blogDao = new BlogDao();
//先尝试获取到req中的blogId参数,如果该参数存在,说明是邀请博客详情
//如果该参数不存在,说明是请求博客的列表
String param = req.getParameter("blogId");
if(param == null){
//不存在参数,获取博客列表
List<Blog> blogs = blogDao.selectAll();
//把blogs对象转换成Json格式
String respJson = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(respJson);
}else{
//存在参数,获取博客详情
int blogId = Integer.parseInt(param);
Blog blog = blogDao.selectOne(blogId);
String respJson = objectMapper.writeValueAsString(blog);
resp.getWriter().write(respJson);
}
}
重写doPost方法,这个方法用于提交博客
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(false);
resp.setContentType("text/html;charset=utf8");
if(session == null){
//当前用户未登录,不能提交博客
resp.getWriter().write("当前用户未登录,不能提交博客");
return;
}
User user = (User)session.getAttribute("user");
if(user == null){
//当前用户未登录
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.getWriter().write("提交失败,缺少必要的参数");
return;
}
//构造blog对象,把当前的信息填进去,并插入数据库中
//此处要给blog设置属性title,content,useId
//postTime和blogId都不需要手动指定,都是插入数据库的时候自动生成的
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
resp.sendRedirect("blog_list.html");
}
3)编写客户端代码
在页面加载的时候,通过ajax给服务器发送数据,获取到博客列表信息,并且显示在界面上。获取到的body就是一个js的数组对象,每个元素就是一个js对象,根据这个对象构造div。
构造div步骤:
- 先把.right里面原有的内容给清空
- 遍历body,构造一个个bodyDiv
- 把blogDiv挂到dom树上
blog_list.html
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script>
//在页面加载的时候,通过ajax给服务器发送数据,获取到博客列表信息,并且显示在界面上
function getBlogList(){
$.ajax({
type: 'get',
url: 'blog',
success: function(body){
//获取到的body就是一个js的数组对象,每个元素就是一个js对象,根据这个对象构造div
//1.先把.right里面原有的内容给清空
let rightDiv = document.querySelector('.right');
rightDiv.innerHTML = '';
//2.遍历body,构造一个个bodyDiv
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挂到dom树上
rightDiv.appendChild(blogDiv);
}
},
error: function(){
alert("获取博客列表失败");
}
});
}
6.实现博客详细界面
1)约定前后端交互接口
这里和获取客列表的区别:
- 请求里面带有参数blogId
- 响应结果不是json数组了,而是单一的对象
- 响应的博客正文不再截断了
2)编写服务器代码
重写doGet方法,这个方法用来获取到数据库的博客详情
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json; charset=utf-8");
BlogDao blogDao = new BlogDao();
//先尝试获取到req中的blogId参数,如果该参数存在,说明是邀请博客详情
//如果该参数不存在,说明是请求博客的列表
String param = req.getParameter("blogId");
if(param == null){
//不存在参数,获取博客列表
List<Blog> blogs = blogDao.selectAll();
//把blogs对象转换成Json格式
String respJson = objectMapper.writeValueAsString(blogs);
resp.getWriter().write(respJson);
}else{
//存在参数,获取博客详情
int blogId = Integer.parseInt(param);
Blog blog = blogDao.selectOne(blogId);
String respJson = objectMapper.writeValueAsString(blog);
resp.getWriter().write(respJson);
}
}
3)编写客户端代码
在客户端这边想要构造一个请求获取博客详情就得知道当前用户点击的博客id,这个id就包含在当前blog_detail.html页面的url里面了。具体映射关系如下图:
这个参数该如何构造如下图所示:
location.serach的来源与博客列表的的代码:
在博客列表页,从服务器获取博客列表的嘶吼,从服务器拿到了blog的信息。基于这个信息来构造页面中的内容。在“查看全文”a标签按钮构造的时候,就给href填充了当前博客的blogId。
除此之外,还有一个重要的的问题,当前博客正文并不单纯地文本,而是带有一定格式的markdown数据。因此需要使用editor.md库来完成,这个库不仅提供了markdown编辑器也提供了渲染功能。
function getBlogDetail() {
$.ajax({
type:'get',
//location.search拿到了形如'?blogId=5'这样的一段内容
url:'blog' + location.search,
success:function (body) {
//根据body中的内容来构造页面
//1.构造博客标题
let h3 = document.querySelector(".blog-content>h3");
h3.innerHTML = body.title;
//2.构造博客发布时间
let dateDiv = document.querySelector('.date');
dateDiv.innerHTML = body.postTime;
// 3. 构造博客正文
// 第一个参数对应 id=content 的 html 标签. 渲染后得到的 html 片段就会被放到这个 标签下.
editormd.markdownToHTML('content', {
markdown: body.content
});
}
});
7.实现博客登陆页面
1)约定前后端交互接口
当登陆成功后就自动跳转到主页(博客列表页)
2)编写服务器代码
登录部分逻辑为:
- 获取请求中的参数
- 和数据库中的内容进行比较
- 如果通过比较就创建会话
@WebServlet("/login")
public class loginServlet extends HttpServlet {
private ObjectMapper objectMapper = new ObjectMapper();
@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");
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");
}
}
3)编写客户端代码
这里使用form表单来发送请求,具体实现如下
<!DOCTYPE html>
<html lang="en">
<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>
<link rel="stylesheet" href="css/common.css">
<link rel="stylesheet" href="css/blog_login.css">
</head>
<body>
<!-- 这是导航栏 -->
<div class="nav">
<img src="image/logo.jpg">
<span>我的博客系统</span>
<!-- 空白元素, 用来占位置 -->
<div class="spacer"></div>
<!-- <a>注销</a> -->
</div>
<div class="login-container">
<form action="login" method="post">
<div class="login-dialog">
<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">
<!-- <button>提交</button> -->
<input type="submit" id="submit" value="提交">
</div>
</div>
</form>
</div>
</body>
</html>
8.实现用户状态判定功能
实现用户状态的判定用于限制用户的操作,未登录状态下不可访问博客列表、查看博客详情以及进行博客编辑和发布操作。
1)约定前后端交互接口
登陆了就这节返回当前登录的用户信息,未登录就直接返回一个userId为0的对象
2)编写服务器代码
重写doGet方法用来让前端检测当前的登录状态
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("application/json;charset=utf8");
req.setCharacterEncoding("utf8");
resp.setCharacterEncoding("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));
}
3)common.js逻辑实现部分
通过GET/login这个请求来获取下当前的登录状态,登陆成功不做处理,登录不成功返回到登录页面
//加上一个逻辑,通过GET/login这个请求来获取下当前的登录状态
function getUserInfo(pageName){
$.ajax({
type:'get',
url:'login',
success: function(body){
//判定此处的body是不是一个有效的user对象(userId是否非0)
if(body.userId && body.userId > 0){
//登陆成功不做处理
//根据用户登陆情况,把当前用户名设置到界面上
if(pageName == 'blog_list.html'){
changeUserName(body.username);
}
}else{
alert("当前您尚未登录!请登录后再访问博客列表");
location.assign('blog_login.html');
}
},
error: function(){
alert("当前您尚未登录!请登录后再访问博客列表");
location.assign('blog_login.html');
}
});
}
function changeUserName(username){
let h3 = document.querySelector('.card>h3');
h3.innerHTML = username;
}
9.实现显示用户信息功能
显示用户信息逻辑在于在博客列表页,期望显示的用户信息就是当前登录用户。而在博客详情页,期望显示的用户信息是当前文章的作者。
1.针对博客列表页
在监测用户登录状态的时候已经进行过处理
2.针对博客详情页
让服务器提供一个新的接口,这个接口可以让客户端指定blodId,获取到指定blogId的作者信息
1)约定前后端交互接口
2)编写服务器代码
这里的逻辑很简单,主要是UserDao的方法调用和条件判定。根据当前blogId在数据库中查找,找到对应的blog对象,在进一步根据blog对象,找到作者信息。
public class AuthorServlet 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 param = req.getParameter("blogId");
if(param == null || "".equals(param)){
//参数少了
resp.getWriter().write("{ \"ok\": false, \"reason\": \"参数缺失!\" }");
return;
}
//根据当前blogId在数据库中查找,找到对应的blog对象,在进一步根据blog对象,找到作者信息
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectOne(Integer.parseInt(param));
if(blog == null){
resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的博客不存在!\" }" );
return;
}
//根据blog对象查询到用户对象
UserDao userDao = new UserDao();
User author = userDao.selectById(blog.getUserId());
if(author == null){
resp.getWriter().write("{ \"ok\": false, \"reason\": \"要查询的博客不存在!\" }");
return;
}
author.setPassword("");
resp.getWriter().write(objectMapper.writeValueAsString(author));
}
}
3)blog_detail逻辑实现部分
从服务器获取一下当前博客的作者信息, 并显示到界面上,参数 user 就是刚才从服务器拿到的当前登录用户的信息。
function getAuthorInfo(user) {
$.ajax({
type: 'get',
url: 'authorInfo' + location.search,
success: function(body) {
// 此处的 body, 就是服务器返回的 User 对象, 是文章的作者信息
if (body.username) {
// 如果响应中的 username 存在, 就把这个值设置到页面上.
changeUserName(body.username);
if (body.username == user.username) {
// 作者和登录的用户是一个人, 则显示 "删除按钮"
let navDiv = document.querySelector('.nav');
let a = document.createElement('a');
a.innerHTML = '删除';
// 期望点击删除, 构造一个形如 blogDelete?blogId=6 这样的请求
a.href = 'blogDelete' + location.search;
navDiv.appendChild(a);
}
} else {
console.log("获取作者信息失败! " + body.reason);
}
}
});
}
10.实现注销功能
退出当前登录状态,在导航栏中安排一个“注销”按钮,当用户点击注销之后,就会在服务器上取消登录状态,并且能够跳转到登录页面。
1)约定前后端交互接口
2)编写服务器代码
登录状态时,用户有一个session,同时session有一个user属性,两者同时具备才叫登录状态。
注销只需要破坏掉任意一个条件就可以了,此处代码选择破坏user属性,把user属性从session中删除。
@WebServlet("/logout")
public class LogoutServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(false);
resp.setContentType("text/html; charset=utf8");
if(session == null){
//用户没有登录
resp.getWriter().write("当前用户尚未登录,无法注销");
return;
}
//把用户会话中的信息删除就行
session.removeAttribute("user");
resp.sendRedirect("blog_login.html");
}
}
3)编写客户端代码
把博客列表页,博客详情页,博客编辑页导航栏中注销按钮中的href属性都做出修改,改成“logout”路径,需要注意的是登录页没有注销按钮。
<a href="logout">注销</a>
11.实现发布博客功能
在博客编辑页中,当用户输入博客标题,和正文之后,点击发布此时就会把博客数据提交到服务器,由服务器存储到数据中
1)约定前后端交互接口
2)编写服务器代码
在BlogServlet里面添加一个doPost方法,来处理上述post请求。核心操作就是读取请求中的标题和正文,构造Blog对象并插入数据库中。
//这个方法用于提交博客
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
HttpSession session = req.getSession(false);
resp.setContentType("text/html;charset=utf8");
if(session == null){
//当前用户未登录,不能提交博客
resp.getWriter().write("当前用户未登录,不能提交博客");
return;
}
User user = (User)session.getAttribute("user");
if(user == null){
//当前用户未登录
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.getWriter().write("提交失败,缺少必要的参数");
return;
}
//构造blog对象,把当前的信息填进去,并插入数据库中
//此处要给blog设置属性title,content,useId
//postTime和blogId都不需要手动指定,都是插入数据库的时候自动生成的
Blog blog = new Blog();
blog.setTitle(title);
blog.setContent(content);
blog.setUserId(user.getUserId());
BlogDao blogDao = new BlogDao();
blogDao.insert(blog);
resp.sendRedirect("blog_list.html");
}
3)编写客户端代码
<body>
<!-- 这是导航栏 -->
<div class="nav">
<img src="image/logo.jpg" alt="">
<span>我的博客系统</span>
<!-- 空白元素, 用来占位置 -->
<div class="spacer"></div>
<a href="blog_list.html">主页</a>
<a href="blog_edit.html">写博客</a>
<a href="logout">注销</a>
</div>
<!-- 包裹整个博客编辑页内容的顶级容器 -->
<div class="blog-edit-container">
<form action="blog" method="post" style="height:100%">
<div class="title">
<input type="text" placeholder="在此处输入标题" name="title" id="title">
<!-- <button>发布文章</button> -->
<input type="submit" value="发布文章" id="submit">
</div>
<!-- 放置 md 编辑器 -->
<div id="editor">
<!-- 为了进行 form 的提交, 此处搞一个 textarea 多行编辑框, 借助这个编辑框来实现表单的提交 -->
<!-- 可以设置 editor.md, 让编辑器把 markdown 内容也同步的保存到这个隐藏的 textarea 中, 从而可以进行 form 提交 -->
<textarea name="content" style="display:none"></textarea>
</div>
</form>
</div>
<script>
// 初始化编辑器
let editor = editormd("editor", {
// 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
width: "100%",
// 设定编辑器高度
height: "calc(100% - 50px)",
// 编辑器中的初始内容
markdown: "# 在这里写下一篇博客",
// 指定 editor.md 依赖的插件路径
path: "editor.md/lib/",
// 此处要加上一个重要的选项, 然后 editor.md 就会自动把用户在编辑器输入的内容同步保存到 隐藏的 textarea 中了!
saveHTMLToTextarea: true,
});
</script>
</body>
12.实现删除博客功能
这里的逻辑用户只能删除自己的博客不能删除别人的博客。
客户端:
需要在博客详情页这里进行判定,当前洪湖是否就是博客的作者,如果不是就不显示删除按钮。
服务器:
用户点击删除按钮,会触发一个HTTP请求,请求内容爆指定删除的博客id,服务器收到请求后就会把这个博客从数据库中删掉。
1)约定前后端交互接口
2)编写服务器代码
@WebServlet("/blogDelete")
public class BlogDeleteServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.检查用户是否登录
HttpSession session = req.getSession(false);
resp.setContentType("text/html; charset=utf8");
if(session == null){
resp.getWriter().write("当前尚未登录,不能删除");
return;
}
User user = (User) session.getAttribute("user");
if(user == null){
resp.getWriter().write("当前尚未登录,不能删除");
return;
}
//2.获取到参数中的blogId
String blogId = req.getParameter("blogId");
if(blogId == null || "".equals(blogId)){
resp.getWriter().write("当前blogId参数不对");
return;
}
//3.获取要删除的博客信息
BlogDao blogDao = new BlogDao();
Blog blog = blogDao.selectOne(Integer.parseInt(blogId));
if(blog == null){
resp.getWriter().write("当前要删除的博客不存在");
return;
}
//4.再次校验,当前的用户是否就是博客的作者
if(user.getUserId() != blog.getUserId()){
resp.getWriter().write("当前登录用户不是作者没有权限");
}
//5.确认无误,开始删除
blogDao.delete(blog.getBlogId());
//6.重定向到博客列表页
resp.sendRedirect("blog_list.html");
}
}
3)编写客户端代码
这是关键代码,要在第一个ajax的灰调中执行第二个ajax,才能保证两个ajax之间的获取数据的顺序先后。