【Servlet综合项目练习】实现一个简单的博客系统~

news2024/11/12 4:32:19

目录

🌟一、数据库设计部分

1、建表分析:系统中一共要实现几张表? 

2、开始建表

🌟二、大概框架与实现功能

🌟 三、代码实现部分

🌈前言1:工具类的实现(utils包下)

1、数据库连接的工具类

2 、 用户信息判空的工具类

3、判断当前用户是否已经登录的工具类

🌈前言2:java实体类的定义(model包下)

1、User实体类

2、 Blog实体类

🌈前言3:AppResult类定义响应的返回格式json(Common包下)

🌈1、实现登录功能

1.1 UserDao下:selectByUsername通过用户名查询对应用户

1.2 Servlet实现后端

1.3 前端实现 blog_login.html

🌈2、获取当前用户的登录信息

2.1 Servlet实现后端

2.2 前端代码实现:blog_list.html

🌈3、获取当前所有的文章列表

3.1 BlogDao:selectAll在数据库中查询所有的博客记录

3.2 Servlet实现后端

 3.3 前端实现:blog_list.html

🌈4、查看全文功能

界面1实现 :显示这篇博客的具体内容

4.1 UserDao下 :User selectById(Long id)通过Id获取对应用户信息

4.2 BolgDao下:Blog selectById(Long Id)通过Id查询对应博客

4.3 Servlet实现后端

4.4 前端实现:blog_details.html

界面2实现 :显示用户信息

4.1 Servlet实现后端

4.2 前端实现 blog_details.html

🌈 5、发布博客

 5.1 blogDao下:insert(Blog blog)写入博客到数据库

5.2 Servlet实现后端

 5.3 前端实现 blog_eidt.html

🌈6、删除当前文章

6.1 BlogDao下:deleteById(Long id)通过Id删除博客

6.2 Servlet实现后端

(1)更新后的UserServlet

(2)blogServlet中实现doDelete方法

6.3  前端实现:blog_details.html

🌈7、注销功能

7.1 Servlet实现后端

7.2 前端实现 blog_list.html


🌟一、数据库设计部分

1、建表分析:系统中一共要实现几张表? 

分析:系统中一共要实现几张表?

        注意:凡是不能被数据库或者编程系统中的基本数据类型表示的,都可以考虑成是一个类或者是一张表。比如:学生类:学生有Id,姓名,成绩...就可以设计为一张表,这个学生对象就可以设计为一个类,类的属性和表的字段一一对应。

1、用户表:一共有四个字段(后面根据博客功能实现,又新增了一个属性:是否为同一个用户)

(1)用户名

(2)密码

(3)Github地址

(4)发表的文章数量

2、博客表

(1)标题

(2)发布内容

(3)发布时间

(4)作者Id(与用户表关联)

2、开始建表

#创建数据库  
create database blog_db char set utf8mb4 collate utf8mb4_general_ci;

#选择数据库
use blog_db;
#建立用户表
create table user(
	id bigint primary key auto_increment comment'用户Id,自增类型',
    username varchar(50) unique not null comment'用户名',
    password varchar(50) not null comment '密码'    
);
# 查看表是否建立成功
select * from user;

# 建立博客表
use blog_db;
create table blog(
	id bigint primary key auto_increment comment'id,自增类型',
    title varchar(1024) not null comment'标题',
    content text not null comment'内容',
    createTime datetime not null comment'发布时间',
    userId bigint not null comment'用户名'
);
select * from blog;

最终建表效果


        由于不实现注册功能,所以这里我们给它初始化两个用户,方便后续直接进行登录。

use blog_db;
insert into user values (null,'小花花','123456');
insert into user values (null,'小叶子','123456');

blog表中也初始化两条数据方便后续使用;

🌟二、大概框架与实现功能

        在具体开始实现代码之前,我们首先要分析出做的这个Blog系统大概都有什么样的功能。我们将实现的功能总结如下:

(1)登录功能

(2)获取当前用户的登录信息

(3)获取当前所有的文章列表

(4)查看文章详情

(5)写博客

(6)退出登录


在实现代码的过程中,为了方便管理,我们将不同功能的代码放在不同的包下,主要分为五个包:

(1)Utils:存放各种工具类

(2)model:存放Java实体类,根据数据库中的表创建各种实体类

(3)dao:负责实体类与数据库之间的联系

(4)Servlet:后端代码的实现

(5)common:存放公共类,用于响应的时候以Json格式返回。

        一般Utils,model提前写好,commom中随用随写,真正要写的代码:先写Dao中的,从数据库中查询内容——>写Servlet中的后端代码:从前端获取数据进行处理并返回响应,在这个过程中一般会用到Dao包中从数据库中获取到的内容——>写前端html中的代码:主要是从控件中获取内容用ajax发送数据给后端,并通过回调函数来处理后端的响应。(自己的一点点感悟~~有一点点写代码的感觉,但不多...哈哈哈)简单知道这些之后,接下来我们就可以愉快的写代码啦~~

🌟 三、代码实现部分

🌈前言1:工具类的实现(utils包下)

1、数据库连接的工具类

        建立用户表和博客表之后,后续要进行数据库的连接等操作,所以在这里我们将数据库的部分功能进行封装,方便后续直接使用。

public class DBUtils {
    //1、定义数据源
    private static DataSource dataSource;
    //定义三个属性
    private static final String URL = "jdbc:mysql://localhost:3306/blog_db?characterEncoding=utf8&useSSL=false";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";
    //2、完成数据的初始化
    static {
        MysqlDataSource mysqlDataSource = new MysqlDataSource();
        mysqlDataSource.setURL(URL);
        mysqlDataSource.setUser(USER);
        mysqlDataSource.setPassword(PASSWORD);
        dataSource = mysqlDataSource;
    }
    //3、构造方法私有化
    private DBUtils(){};
    //4、构造一个连接
    public static Connection getConnection() throws SQLException {
        return dataSource.getConnection();
    }
    //5、依次关闭连接,释放资源
    public static void close(PreparedStatement statement, Connection connection, 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 、 用户信息判空的工具类

用户信息的判空,封装成一个类:检验字符串是否为空。

public class StringUtils {
    public static boolean isEmpty(String value){
        if(value == null || value.equals("")){
            //true表示为空
            return true;
        }
        //不为空:返回false
        return false;
    }
}

3、判断当前用户是否已经登录的工具类

        在完成登录之后,后续所有的子页面的各种操作都是在保证当前用户已经登录的情况下完成的,所以将用户是否登录封装为一个类。

public class UserUtils {
    public static User checkUserLoginStatus(HttpServletRequest request){
        //先判断req是否为空
        if(request == null){
            return null;
        }
        //否则获取session
        HttpSession session = request.getSession(false);
        //获取用户对象
        User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
        return user;
    }
}

🌈前言2:java实体类的定义(model包下)

实体类的每个属性都和数据库表中的字段名一一对应。isAuthor属性后面会介绍到。

1、User实体类

public class User {
    //三个属性和数据库表中的字段要对应
    private Long id;
    private String username;
    @JsonIgnore//不参与json序列化
    private String password;
    
    private boolean isAuthor;

    //无参构造
    public User(){}
    //提供相应的get和set方法
    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    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;
    }
    
    public boolean isAuthor() {
        return isAuthor;
    }

    public void setAuthor(boolean author) {
        isAuthor = author;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}

2、 Blog实体类

public class Blog {
    private Long id;
    private String title;
    private String content;
    private Timestamp createTime;//发布时间,类型 java.sql.Timestamp
    private Long userId;//用户id

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public Timestamp getCreateTime() {
        return createTime;
    }

    public void setCreateTime(Timestamp createTime) {
        this.createTime = createTime;
    }

    public Long getUserId() {
        return userId;
    }

    public void setUserId(Long userId) {
        this.userId = userId;
    }
    @Override
    public String toString() {
        return "Blog{" +
                "id=" + id +
                ", title='" + title + '\'' +
                ", content='" + content + '\'' +
                ", createTime=" + createTime +
                ", userId=" + userId +
                '}';
    }
}

🌈前言3:AppResult类定义响应的返回格式json(Common包下)

第一点: 实现一个统一的数据返回格式,定义一个AppResult类来实现。


第二点:

/**
 * 响应的时候以json的形式返回,这里定义一个共用的类
 */
public class AppResult<T> {
    //用于描述状态码的属性
    private int code;
    //描述信息
    private String message;
    //返回数据:泛型当类里面传的是User,后面data里面存的就是User
    private T data;
    
public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    
    //三个参数的构造方法
    public AppResult(int code, String message, T data) {
        this.code = code;
        this.message = message;
        this.data = data;
    }
    //工厂方法
    /**
     * 普通的成功方法
     * @return
     */
    public static AppResult success(){
        return new AppResult(0,"操作成功",null);
    }
    public static AppResult success(String message){
        return new AppResult(1,message,null);
    }
    public static <T>AppResult success(T data){
        return new AppResult(0,"操作成功",data);
    }
    public static <T>AppResult success(String message,T data){
        return new AppResult(0,message,data);
    }

    /**
     * 失败方法
     * @return
     */
    public static AppResult failed(){
        return new AppResult(1000,"操作失败",null);
    }
    public static AppResult failed(String message){
        return new AppResult(1000,message,null);
    }
}
//定义全局的配置变量
public class AppConfig {
    public static final String USER_SESSION_KEY = "USER_SESSION_KEY";
}

🌈1、实现登录功能

1.1 UserDao下:selectByUsername通过用户名查询对应用户

        分析:在登录的时候,前端输入用户名和密码,此时我们需要从数据库中获取用户名,判断该用户名是否存在,因此提供一个通过用户名来查询用户信息的数据库访问方法,给定用户名,该用户名在数据库中存在的话,就返回该用户信息(包括id,用户名,密码);否则返回null。

提供一个selectByUsername的数据库访问方法:UserDao.java。

public class UserDao {
    /**
     * 根据用户名查询用户信息
     * @param username 用户名
     * @return 返回一个user对象
     */
    public User selectByUsername(String username){
        //1、判断username是否为空
        if(StringUtils.isEmpty(username)){
            return null;
        }
        //2、说明username有值,连接数据库,查询用户名为username的用户
        //(1)进行数据库连接操作,定义数据库访问的相关对象
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        try {
            //(2)连接数据库
            connection = DBUtils.getConnection();
            //(3)定义sql语句
            String sql = "select id,username,password from user where username = ?";
            //(4)对sql进行预处理
            statement = connection.prepareStatement(sql);
            //(5)设置占位符的值
            statement.setString(1,username);
            //(6)执行sql,获取结果
            resultSet = statement.executeQuery();
            //(7)解析结果集,构造对象
            if(resultSet.next()){
                //创建一个User对象
                User user = new User();
                user.setId(resultSet.getLong(1));
                user.setUsername(resultSet.getString(2));
                user.setPassword(resultSet.getString(3));
                //返回结果
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //3、释放资源
            DBUtils.close(statement,connection,resultSet);
        }
        //4、说明没有查询到结果,返回null
        return null;
    }
}

每次写完一个方法,就测试是否能通过:

1.2 Servlet实现后端

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //定义统一返回的数据类型
        AppResult appResult;
        //2、获取session对象
        HttpSession session = req.getSession(false);
        //3、判断session
        //如果session为空,说明用户没有登录,是不能获取当前的用户信息的
//        if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
//            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
//            resp.setStatus(403);
//            //返回错误信息
//            appResult = AppResult.failed("用户没有登录,请登录后再试!");
//            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//            //中断代码流程
//            return;
//        }
        //3、也可以直接调用工具类的方法
        if(UserUtils.checkUserLoginStatus(req) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            appResult = AppResult.failed("用户没有登录,请登录后再试!");
            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
            //中断代码流程
            return;
        }
        //4、校验成功,获取用户信息
        User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
        //5、设置返回结果中的对象,返回对象
        appResult = AppResult.success(user);
        resp.getWriter().write(objectMapper.writeValueAsString(appResult));
    }
}

Postman测试后端 登录功能

每次写完一个接口,就用Postman测试一下。打开Postman,进行如下操作 

 情况1:用户名和密码为空

 情况2:输入错误的密码

 情况3:输入错误的用户名

情况4:输入正确的用户名和密码

 OK~实现前端代码啦!

1.3 前端实现 blog_login.html

1、前后端交互协议

(1)请求:POST/login

        Content-Type;application/x-www.form-urlencoded

        username = test & password=123456

(2)响应: 返回json格式

        {

                "code":0

                "message":"登录成功"

        }


2、前端现在要做的事情:

(1)往后端login下发送POST请求;

(2)构造  username = test & password=123456形式的数据发送给后端;

(3)根据后端返回的响应做出操作:如果后端返回的是0,说明登录成功,跳转到博客列表页;如果失败,弹出message的错误信息。


3、实现前端:提交数据的方式有两种

(1)通过表单提交

(2)通过ajax提交


4、注意点:

 error表示HTTP响应状态码返回的不是200的时候都会调用。



 <!-- 引入jquery -->
<script src = "./js/jquery-3.6.3.min.js"></script> 
<script>
    // <!-- 在页面加载完成之后再执行jS,目的是保证元素已经初始化成功后再执行后续操作 -->   
    $(function(){
        //4、为登录按钮绑定事件
        $('#btn_login_submit').click(function(){
            //1、获取控件输入的用户名和密码:id选择器
            let usernameEl = $('#username');
            if(!usernameEl.val()){
                alert('用户名不能为空');
                //让用户名输入框获得焦点
                usernameEl.focus();
                return;
            }
            //获取输入的密码并做校验
            let passwordEl = $('#password');
            if(!passwordEl.val()){
                console.log(passwordEl.val());
                alert('请输入密码');
                passwordEl.focus();
                return;
            }
            //2、构造要给后端发送的数据
            let postData = {
                username : usernameEl.val(),
                password : passwordEl.val()
            };
            //3、发送ajax请求
            $.ajax({
                type:'post',
                url: 'login',
                contentType:'application/x-www-form-urlencoded',
                data : postData,
                //成功回调
                success : function(respData){
                    if(respData.code == 0){
                        //登录成功,跳转到博客页面                      
                        location.assign('blog_list.html');
                    }else{
                        //登录失败
                        alert(respData.message);
                    }
                },
                //回调失败
                error : function(){
                    console.log('访问出现问题');
                }
            });
        });
    })
</script>
</html>

🌈2、获取当前用户的登录信息

        在第一步登录功能实现中,当用户登录成功之后,已经将用户的信息保存在了session中,所以直接从session中获取即可。

(1)用户发送请求到URL;

(2)服务器从session中取出用户信息;

(3)转化为Json字符串并返回。

2.1 Servlet实现后端

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //定义统一返回的数据类型
        AppResult appResult;
        //2、获取session对象
        HttpSession session = req.getSession(false);
        //3、判断session
        //如果session为空,说明用户没有登录,是不能获取当前的用户信息的
        if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            appResult = AppResult.failed("用户没有登录,请登录后再试!");
            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
            //中断代码流程
            return;
        }
        //4、校验成功,获取用户信息
        User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
        //5、设置返回结果中的对象,返回对象
        appResult = AppResult.success(user);
        resp.getWriter().write(objectMapper.writeValueAsString(appResult));
    }
}

 1、代码关键点1

Postman测试后端

在Postman中线执行登录流程再获取用户信息。

        但是通过上面的结果我们可以看到,关于用户的隐私字段不应该在网上显示出来,所以在Json序列化的时候就应该排除这个字段。

        序列化:将java对象转化为可以在网络上传输的数据类型,便于在网络上传输,比如字符串,byte数组等等(java->json,java->xml);反序列化:就是将字符串,byte数组,json格式等还原为java对象(json->java,xml->java)。


再次测试:

2.2 前端代码实现:blog_list.html

步骤:

(1)给h3标签(用户名标签)设置id选择器名;

 (2)引入Js依赖

(3)页面加载之后就发送ajax请求 

 (4)在回调函数中处理响应

 statusCode是一个对象,每个状态码表示一个属性,属性的类型是一个function。

部分关键代码实现如下: 

<script src = "js/jquery-3.6.3.min.js"></script> 
<script>
// 目标:只要博客页面一启动,就发送ajax请求给后端,同时回调函数获取响应的用户信息,设置到前端的控件上
    //实现功能2:用户控件上显示用户信息
    $(function(){
        //1、发送ajax请求获取响应数据
        $.ajax({
            type:'get',
            url:'user',
            //回调函数
            //HTTP状态码返回为200的时候调用
            success : function(respData){
                if(respData.code == 0){
                    //成功
                    //获取从后端返回的User信息
                    let user = respData.data;
                    //给前端的控件上设置该用户信息
                    $('#h_list_username').html(user.username);
                }else{
                    //失败
                }
            },
            //HTTP返回状态码不是200的时候都会调用error
            error : function(){

            },
            //403状态可以调用error,但是有一个更加具体的方法statusCode,专门为不同的HTTP状态码定义不同的方法
            statusCode : {
                403:function(){
                    //打印日志并跳转到登录界面
                    console.log('用户无权访问,强制跳转到登录界面');
                    location.assign('blog_login.html');
                }
            }
        });
</script>

🌈3、获取当前所有的文章列表

约定前后端交互接口:

 根据以上响应的格式,在DAO中的sql,应该是从数据库的blog中查询所有,查询出来的是一个集合,集合的每个对象都是一条博客记录,包括id,标题,内容,发布时间,用户id。

3.1 BlogDao:selectAll在数据库中查询所有的博客记录

public class BlogDao {
    /**
     * 获取所有的文章列表
     * @return
     */
    public List<Blog> selectAll(){
        //1、定义数据库访问的相关对象
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        //2、建立数据库连接
        try {
            connection = DBUtils.getConnection();
            //3、定义sql语句:按发布时间排序
            String sql = "select id,title,content,createTime,userId " +
                    "from blog order by createTime desc";
            //4、处理sql
            statement = connection.prepareStatement(sql);
            //5、执行sql语句,获取结果集
            resultSet = statement.executeQuery();
            //6、遍历结果集
            List<Blog> blogList = null;
            while (resultSet.next()){
                //为空的时候初始化一个集合
                if(blogList == null){
                    blogList = new ArrayList<Blog>();
                }
                //集合中的每个元素都是一个blog对象
                Blog blog = new Blog();
                blog.setId(resultSet.getLong(1));
                blog.setTitle(resultSet.getString(2));
                blog.setContent(resultSet.getString(3));
                //判断一下内容长度,如果内容长度大于20,就显示前20个字,否则显示所有
                if(blog.getContent().length()>=20){
                    blog.setContent(blog.getContent().substring(0,20)+"...");
                }
                
                blog.setCreateTime(resultSet.getTimestamp(4));
                blog.setUserId(resultSet.getLong(5));
                //把blog对象加入集合中
                blogList.add(blog);
            }
            //返回结果
            return blogList;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(statement,connection,resultSet);
        }
        return null;
    }
}

 写完之后用测试类测试:

3.2 Servlet实现后端

注意:要实现博客表中所有信息的获取,前提是要保证用户已经登录。

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    //用户数据库访问的Dao
    private BlogDao blogDao = new BlogDao();
    //用于转换Json
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=utf-8");
        //2、前提:要获取所有的博客的前提是:要保证用户已经登录
        //校验用户的登录状态
        if(UserUtils.checkUserLoginStatus(req) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("用户没有登录,请登录后再试!")));
            //中断代码流程
            return;
        }
        //3、获取所有的博客(是个集合)并返回
        List<Blog> blogs = blogDao.selectAll();
        //在这里处理一下:如果集合为空的话,就返回一个空集合[],而不是null的形式
        if(blogs == null){
            blogs = new ArrayList<Blog>();
        }
        //正常返回给前端
        resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success(blogs)));
    }
}

Postman测试后端代码

 进行登录之后,再次测试;

 3.3 前端实现:blog_list.html

注意,在获取时间的时候,要对时间格式进行处理,用一个写好的函数formatDate。

(1)引入js文件

// common.js文件内容
function formatDate(time) {
    var date = new Date(time);

    var year = date.getFullYear(),
        month = date.getMonth() + 1,//月份是从0开始的
        day = date.getDate(),
        hour = date.getHours(),
        min = date.getMinutes(),
        sec = date.getSeconds();
    var newTime = year + '-' +
        (month < 10 ? '0' + month : month) + '-' +
        (day < 10 ? '0' + day : day) + ' ' +
        (hour < 10 ? '0' + hour : hour) + ':' +
        (min < 10 ? '0' + min : min) + ':' +
        (sec < 10 ? '0' + sec : sec);

    return newTime;
}

(2)调用函数

处理之前

处理之后

 

//实现功能3:页面一加载就进行博客列表的获取
        $.ajax({
            type:'get',
            url:'blog',
            //回调方法
            success:function(respData){
                //根据自定义的状态码处理
                if(respData.code == 0){
                    //成功,构造文章列表:构造一个函数
                    buildArticlelList(respData.data);
                }else{
                    //失败
                    alert(respData.message);
                }
            },
            error:function(respData){
                console.log("访问出现错误");
            },            
            statusCode:{
                403:function(){
                    //未登录状态:强制让用户跳转到登录界面
                    location.assign('blog_login.html');
                }
            }
        });
        // 构建文章列表:data是一个集合
        function buildArticlelList(data){           
            //如果集合为空
            if(data.length == 0 || data == null){
                //当文章列表为空时
                let htmlString = '<h3>没有文章,快发布一篇吧!</h3>'
                $('#container-right').html(htmlString);
                return;
            }
            //说明文章列表不为空:遍历集合方式1:
            for(let i=0;i<data.length;i++){
                let blogItem = data[i];
                // 对对应的控件进行相应的赋值
                let htmlString = ' <div class="blog-content">'
                    + ' <div class="blog-title">'
                    +   blogItem.title
                    + ' </div>'
                    + ' <div class="blog-datetime">'
                    +   formatDate(blogItem.createTime)
                    + ' </div>'
                    + ' <div class="content">'
                    +   blogItem.content
                    + ' </div>'
                    + ' <div class="aEl">'
                    + ' <a href="./blog_details.html?blog_Id='+blogItem.id+'">+查看全文 &gt;&gt; </a>'
                    + ' </div>'
                    + ' <hr>'                   
                    + ' </div>';
                    // console.log(htmlString);
                    //这里用append,表示追加元素  .html()表示只添加一条数据
                $('.container-right').append(htmlString);
            }
            //遍历集合方式2:data是要遍历的数组或集合
            // data.forEach(element=>{
            //     //element相当于data[i]
            // })            
        }

🌈4、查看全文功能

        实现功能:点击“查看全文”按钮会调转到新的blog_details.html页面。这里需要明确当前要查看的是哪一个文章,此时就要从数据库中查询出文章id作为参数传入,在前端对应代码如下: 

此时在博客子页面,显示的界面如下:

界面1:右侧显示这篇博客的具体内容——>需要根据文章Id查询文章详情——>在BlogDao中实现一个Blog selectById(Long Id)方法;

界面2:左侧实现这篇博客的作者——>需要根据用户Id查询用户信息——>在UserDao中实现一个User selectById(Long Id)方法。

界面1实现 :显示这篇博客的具体内容

4.1 UserDao下 :User selectById(Long id)通过Id获取对应用户信息

/**
     * 根据用户Id查询用户信息
     * @param id 用户信息
     * @return 对应的用户记录
     */
public User selectById(Long id){
        //判断id是否为空
        if(id == null || id <=0){
            return null;
        }
        //连接数据库获取查询数据
        //(1)初始化数据库对象
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        //(2)获取数据库连接
        try {
            connection = DBUtils.getConnection();
            //(3)定义sql
            String sql = "select id,username,password from user where id = ?";
            //(4)预处理sql
            statement = connection.prepareStatement(sql);
            //(5)设置占位符
            statement.setLong(1,id);
            //(6)获取结果集
            resultSet = statement.executeQuery();
            //(7)获取结果集
            if(resultSet.next()){
                //(8)封装对象:创建一个用户对象并设置参数
                User user = new User();
                user.setId(resultSet.getLong(1));
                user.setUsername(resultSet.getString(2));
                user.setPassword(resultSet.getString(3));
                //(9)返回结果
                return user;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(statement,connection,resultSet);
        }
        return null;
    }

测试代码

4.2 BolgDao下:Blog selectById(Long Id)通过Id查询对应博客

/**
     * 根据博客Id查询对应的博客信息
     * @param id 博客Id
     * @return 一条博客记录
     */
    public Blog selectById(Long id){
        //非空校验
        if(id == null || id <= 0){
            return null;
        }
        //数据库操作
        //(1)定义数据库对象
        Connection connection = null;
        PreparedStatement statement = null;
        ResultSet resultSet = null;
        //(2)获取数据库连接
        try {
            connection = DBUtils.getConnection();
            //(3)定义sql语句
            String sql = "select id,title,content,createTime,userId from blog where id =?";
            //(4)预处理sql
            statement = connection.prepareStatement(sql);
            //(5)设置占位符
            statement.setLong(1,id);
            //(6)执行sql
            resultSet = statement.executeQuery();
            //(7)获取结果集
            if(resultSet.next()){
                //封装Blog对象
                Blog blog = new Blog();
                blog.setId(resultSet.getLong(1));
                blog.setTitle(resultSet.getString(2));
                blog.setContent(resultSet.getString(3));
                blog.setCreateTime(resultSet.getTimestamp(4));
                blog.setUserId(resultSet.getLong(5));
                //(8)返回结果
                return blog;
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(statement,connection,resultSet);
        }
        return null;
    }

测试

4.3 Servlet实现后端

        之前我们在实现“获取所有的博客列表”时,BlogServlet中写了一个doGet方法是直接获取所有的博客记录。所以现在我们的目标是,传入id,返回一个blog记录,也是一个doGet方法。所以我们现在有两种方法,重新实现一个新的Servlet的doGet方法,接收blogId参数,返回对应的博客内容,还有一种是在我们现有的BlogServlet代码中,判断有没有传进来参数blogId,如果有的话,就执行新的方法,返回对应的博客记录;否则就执行之前的selectAll方法,查询出所有的博客列表信息。在这里我们采用后面这种方式实现。

更新后的BlogServlet代码

@WebServlet("/blog")
public class BlogServlet extends HttpServlet {
    //用户数据库访问的Dao
    private BlogDao blogDao = new BlogDao();
    //用于转换Json
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=utf-8");
        //2、前提:要获取所有的博客的前提是:要保证用户已经登录
        //校验用户的登录状态
        if(UserUtils.checkUserLoginStatus(req) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("用户没有登录,请登录后再试!")));
            //中断代码流程
            return;
        }
        //-------------功能4实现:在原先基础上实现:如果传入blogId参数,就返回对应的博客信息,否则返回所有的博客列表信息
        //获取blogId参数
        String blogId = req.getParameter("blog_Id");
        //定义返回的Json变量
        String jsonStr = null;
        //判断参数是否为空:如果是空的话,就查询所有的博客列表
        if(StringUtils.isEmpty(blogId)){
            //3、获取所有的博客(是个集合)并返回
            List<Blog> blogs = blogDao.selectAll();
            //在这里处理一下:如果集合为空的话,就返回一个空集合[],而不是null的形式
            if(blogs == null){
                blogs = new ArrayList<Blog>();
            }
            //序列化
            jsonStr = objectMapper.writeValueAsString(AppResult.success(blogs));
        }else{
            //否则根据传入的blogId查询对应的博客信息
            Blog blog = blogDao.selectById(Long.valueOf(blogId));
            //序列化成字符串
            jsonStr = objectMapper.writeValueAsString(AppResult.success(blog));
        }
        //正常返回给前端
        resp.getWriter().write(jsonStr);
    }
}


问题:

怎么从前端将blogId的参数传递给后端?注意代码中的blog_Id在前后端之间的联系,是怎么将其传递给后端的。 就像我们在网址栏输入地址后会出现一个参数。

 方法:

 在postman中测试:在url后面直接拼接?blog_Id=5的参数,可以成功查询。(可以查询成功的是blog_Id是5和4,其他的都不能成功)

上述总结:前端可以通过JS中的location对象获取浏览器地址栏中query string中的参数列表。

 接下来在前端实现代码。

4.4 前端实现:blog_details.html

<!--引入Jquery-->
<script src = 'js/jquery-3.6.3.min.js'></script>
<script src = 'js/common.js'></script>
<!-- 再引入编辑器插件 -->
<script src="./editor.md/editormd.min.js"></script>
<script src="editor.md/lib/marked.min.js"></script>
<script src="editor.md/lib/prettify.min.js"></script>
<script src="editor.md/editormd.js"></script>
<script>
    $(function(){
        //动态为标签赋值
        //获取当前博客的详细信息
        $.ajax({
            type:'get',
            url:'blog' + location.search,//这里的blog对应servlet中的相对应@WebServlet后的"/blog"
            //回调函数
            success : function(respData){                
                if(respData.code == 0){
                    //成功,为页面中的相应标签赋值
                    //查询
                    let blog = respData.data;
                    //赋值
                    $('#div_datails_title').html(blog.title);
                    $('#div_datails_createTime').html(formatDate(blog.createTime));
                    // $('#div_datails_content').html(blog.content);
                    // 在这里对内容做markdown格式处理
                    editormd.markdownToHTML('div_datails_content',{markdown:blog.content})
                }else{
                    //错误
                    alert(respData.message);
                }
            },
            error : function(){
                //打印日志
                console.log('访问出现错误');
            },
            statusCode : {
                403 : function(){
                    //强制跳转到登录界面
                    location.assign('blog_login.html')
                }
            }
        })
    })


</script>

注意点

界面2实现 :显示用户信息

4.1 Servlet实现后端

        注意这里的用户信息不能从sessio获取,session中获取的是当前登录的用户信息,是唯一且固定的,但是现在显示的是每个博客的作者,也就是博客是谁写的。因为我们一开始在数据库设计的时候,blog表中的useId其实就是对应user表中的id,因此可以查询出当前的用户名,显示在前端控件上。所以这里的用户信息是从数据库中获取的。

所以在表中设立关联字段的目的就是为了与其他表建立联系。


做法:和上面的BlogServlet的实现过程相同,可以通过新实现一个Servlet,也可以修改现有的UserServlet,判断是否有参数传入来执行不同的接口。

更新后的UserServlet代码

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    private UserDao userDao = new UserDao();
    private BlogDao blogDao = new BlogDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //定义统一返回的数据类型
        AppResult appResult;
        //2、获取session对象
        HttpSession session = req.getSession(false);
        //3、判断session
        //如果session为空,说明用户没有登录,是不能获取当前的用户信息的
//        if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
//            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
//            resp.setStatus(403);
//            //返回错误信息
//            appResult = AppResult.failed("用户没有登录,请登录后再试!");
//            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//            //中断代码流程
//            return;
//        }
        //3、也可以直接调用工具类的方法
        if(UserUtils.checkUserLoginStatus(req) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            appResult = AppResult.failed("用户没有登录,请登录后再试!");
            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
            //中断代码流程
            return;
        }
        //-----功能4实现:子页面对应显示用户信息:根据是否传入参数来判断执行哪个方法
        String blogId = req.getParameter("blog_Id");
        //定义返回的json格式
        String jsonStr = null;
        //(1)如果当前没有传入参数
        if(StringUtils.isEmpty(blogId)){
            //校验成功,获取用户信息
            User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
            //设置返回结果中的对象,返回对象
            jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
        } else{
            //(2)说明当前传入了参数
            //先查询博客信息
            Blog blog = blogDao.selectById(Long.valueOf(blogId));
            //查询到的博客为空
            if(blog == null){
                jsonStr = objectMapper.writeValueAsString(AppResult.failed("没有找到对应的博客"));
                resp.getWriter().write(jsonStr);
                return;
            }
            //说明查到了博客信息,则根据博客表中的userId查询用户
            User user = userDao.selectById(blog.getUserId());
            //判断user是否为空
            if(user == null){
                jsonStr = objectMapper.writeValueAsString(AppResult.failed("没有找到对应的用户"));
                resp.getWriter().write(jsonStr);
                return;
            }
            //返回用户作者
            jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
        }
        resp.getWriter().write(jsonStr);
    }
}

postman测试

情况1:给定一个存在的博客记录

情况2:给定一条不存在的博客记录

4.2 前端实现 blog_details.html

控件赋值的时候要与标签对应。

 

      //获取作者信息
        $.ajax({
            type:'get',
            url:'user'+location.search,
            //回调函数
            success : function(respData){
                //成功
                if(respData.code == 0){
                    let user = respData.data;
                    //为控件赋值
                    $('#h_details_username').html(user.username);
                }else{
                    alert(respData.message);
                }
            },
            error: function(){
                //打印日志
                console.log('访问出现问题');
            },
            statusCode :{
                403 : function(){
                    //强制跳转到登录界面
                    location.assign(blog_login.html);
                }
            }
        });
    })

前端:

 博客列表页面下:blog_list.html中与后端BlogServlet中的blog_Id要对应。


🌈 5、发布博客

(1)dao包下:在blogDao中实现:sql要做的事情,将新写入的内容用Insert(Blog blog)语句插入到数据库中;

(2)Servlet中对提交的参数做非空校验,并且从sessio中获取用户信息(作者)->博客作者就是当前系统中的登录用户;

(3)前端发送post请求,将用户在页面上输入的数据提交到服务器;成功之后,前端跳转到list界面,展示相应的blog页面。

 5.1 blogDao下:insert(Blog blog)写入博客到数据库

/**
     * 发布一篇新的博客
     * @param blog Blog对象
     * @return 受影响的行数
     */
    public int insert(Blog blog){
        //1、非空校验
        if(blog == null || StringUtils.isEmpty(blog.getTitle())
                || StringUtils.isEmpty(blog.getContent())
                || blog.getUserId() == null
                || blog.getCreateTime() == null){
            return 0;
        }
        //数据库操作
        //(1)定义操作数据库过程中的变量
        Connection connection = null;
        PreparedStatement statement = null;
        //(2)创建数据库连接
        try {
            connection = DBUtils.getConnection();
            //(3)定义SQL语句
            String sql = "insert into blog values (null,?,?,?,?);";
            //(4)预处理sql
            statement = connection.prepareStatement(sql);
            //(5)设置占位符的值
            statement.setString(1,blog.getTitle());
            statement.setString(2,blog.getContent());
            statement.setTimestamp(3,blog.getCreateTime());
            statement.setLong(4,blog.getUserId());
            //(6)执行sql
            int row = statement.executeUpdate();
            //返回结果
            return row;

        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(statement,connection,null);
        }
        return 0;
    }

测试:

在数据库中查询:发现数据写入成功。

5.2 Servlet实现后端

@Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码集
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset=utf-8");
        //获取当前用户的登录信息:从session中获取
        User user = UserUtils.checkUserLoginStatus(req);
        //登录用户的校验:没有登录的用户是不能写博客的
        if(user == null){
            //设置HTTP状态码,用于前端处理不同的响应方法
            resp.setStatus(403);
            //返回错误信息
            String jsonStr = objectMapper.writeValueAsString(AppResult.failed("无权访问,请先登录!"));
            resp.getWriter().write(jsonStr);
            return;
        }
        //说明用户已经登录
        //(1)接收用户提供的参数
        String title = req.getParameter("title");
        String content = req.getParameter("content");
        if(StringUtils.isEmpty(title) || StringUtils.isEmpty(content)){
            String jsonStr = objectMapper.writeValueAsString(AppResult.failed("标题或者正文不能为空"));
            resp.getWriter().write(jsonStr);
        }
        //(2)创建blog对象,传入blogDao中的insert方法
        Blog blog = new Blog();
        blog.setTitle(title);
        blog.setContent(content);
        blog.setCreateTime(new Timestamp(System.currentTimeMillis()));
        //当前登录用户是谁,写博客的作者就是谁
        blog.setUserId(user.getId());
        //(3)调用数据库
        int row = blogDao.insert(blog);
        //(4)判断行数
        if(row <= 0){
            //返回错误信息
            String jsonStr = objectMapper.writeValueAsString(AppResult.failed("保存文章失败,请联系管理员!"));
            resp.getWriter().write(jsonStr);
            return;
        }
        //(5)返回成功信息
        resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("文章发布成功")));
    }

postman测试后端

情况1:标题和内容都正常输入

在数据库查询:发现数据插入成功:

 情况2:当标题和内容为空的时候

 5.3 前端实现 blog_eidt.html

介绍:前端要集成一个编辑器插件: 

(1)引入编辑器样式

(2)引入Jquery

 (3)引入编辑器插件

(4)初始化编辑器

(5)现在我们要在前端输入参数,并获取参数的值。比如 要输入title,我们可以通过input标签获取输入的值;但是现在对于"内容”,在div标签里面生成的值是在editor编辑器里面。所以这里需要进行一点改动,设置隐藏域。

要与获取的控件Id名对应。  


要处理的小细节:

(1)markdown格式问题

上面说这个编辑器是支持markdown格式的,现在我们输入文字并提交;

         返回博客列表页面并查看详情,发现并没有显示markdown格式,因此我们要对这个问题做一下处理。

 在blog_details.html文件下也要引入插件,并对内容做markdown格式处理。(具体代码见4.4节)这样就可以用markdown格式显示内容了。

现在再打开查看详情界面,显示正常。

(2)文字长度问题 

解决办法:blogDao下的selectAll方法,增加一个判断条件。(完整代码见3.1节)

// blog_eidt.html文件下
// 绑定按钮的点击事件
    $('#submit').click(function(){
        // alert(123); 测试按钮是否绑定成功
        //(1)获取用户输入并提交数据
        let titleEl = $('#title');
        let contentEl = $('#text_edit_content');
        // 判断输入的标题和内容是否为空
        if(!titleEl.val()){
            alert('请输入文章标题');            
            return;
        }
        if(!contentEl.val()){
            alert('请输入内容');            
            return;
        }                
        //(2)构造发送的数据
        let postData = {
            title : titleEl.val(),
            content : contentEl.val()
        }
        //(3)提交请求
        $.ajax({
            type : 'post',
            url : 'blog',
            contentType :'application/x-www-form-urlencoded',
            data : postData,
            //回调函数
            success : function(respData){
                //发送成功,则跳转到blog_list.html页面
                if(respData.code == 0){
                    location.assign('blog_list.html');
                }else{
                    alert(respData.message);
                }
            },
            error : function(){
                console.log('访问出现错误!');
            },
            statusCode : {
                403 : function(){
                    //未登录,没有权限访问。强制跳转到登录界面
                    location.assign('blog_login.html');
                }
            }
        })
    })   

🌈6、删除当前文章

删除文章:只能当前用户自己删自己的:

(1)登录人(从session中获取use对象,获取该对象的id)和博客作者(根据传入的blogId)要相同:后端UserServlet实现,返回Booean类型给前端;

(2)前端User类设置一个isAuthor属性,根据根据author是否为True来初始化一个删除按钮并绑定事件;

(3)Dao下:sql的delete方法;

6.1 BlogDao下:deleteById(Long id)通过Id删除博客

/**
     * 删除博客
     * @param id 传入要删除的博客id
     * @return 返回受影响的行数
     */
    public int deleteById(Long id){
        //非空校验
        if(id == null || id == 0){
            return 0;
        }
        //(1)定义sql的对象
        Connection connection = null;
        PreparedStatement statement = null;
        //(2)定义数据库连接
        try {
            connection = DBUtils.getConnection();
            //(3)定义sql语句
            String sql = "delete from blog where id = ?";
            //(4)预处理sql
            statement = connection.prepareStatement(sql);
            //(5)设置占位符
            statement.setLong(1,id);
            //(6)执行sql
            int row = statement.executeUpdate();
            //(7)返回结果
            return row;
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            DBUtils.close(statement,connection,null);
        }
        return 0;
    }

6.2 Servlet实现后端

(1)更新后的UserServlet

主要在之前的基础上:加了下面的代码

@WebServlet("/user")
public class UserServlet extends HttpServlet {
    ObjectMapper objectMapper = new ObjectMapper();
    private UserDao userDao = new UserDao();
    private BlogDao blogDao = new BlogDao();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1、设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //定义统一返回的数据类型
        AppResult appResult;
        //2、获取session对象
        HttpSession session = req.getSession(false);
        //3、判断session
        //如果session为空,说明用户没有登录,是不能获取当前的用户信息的
//        if(session == null || session.getAttribute(AppConfig.USER_SESSION_KEY) == null){
//            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
//            resp.setStatus(403);
//            //返回错误信息
//            appResult = AppResult.failed("用户没有登录,请登录后再试!");
//            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
//            //中断代码流程
//            return;
//        }
        //3、也可以直接调用工具类的方法
        if(UserUtils.checkUserLoginStatus(req) == null){
            //设置HTTP状态码:403表示没有权限访问,在前端的时候会调用到error方法
            resp.setStatus(403);
            //返回错误信息
            appResult = AppResult.failed("用户没有登录,请登录后再试!");
            resp.getWriter().write(objectMapper.writeValueAsString(appResult));
            //中断代码流程
            return;
        }
        //-----功能4实现:子页面对应显示用户信息:根据是否传入参数来判断执行哪个方法
        String blogId = req.getParameter("blog_Id");
        //定义返回的json格式
        String jsonStr = null;
        //(1)如果当前没有传入参数
        if(StringUtils.isEmpty(blogId)){
            //校验成功,获取用户信息
            User user = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
            //设置返回结果中的对象,返回对象
            jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
        } else{
            //(2)说明当前传入了参数
            //先查询博客信息
            Blog blog = blogDao.selectById(Long.valueOf(blogId));
            //查询到的博客为空
            if(blog == null){
                jsonStr = objectMapper.writeValueAsString(AppResult.failed("没有找到对应的博客"));
                resp.getWriter().write(jsonStr);
                return;
            }
            //说明查到了博客信息,则根据博客表中的userId查询用户
            User user = userDao.selectById(blog.getUserId());
            //判断user是否为空
            if(user == null){
                jsonStr = objectMapper.writeValueAsString(AppResult.failed("没有找到对应的用户"));
                resp.getWriter().write(jsonStr);
                return;
            }
            //判断当前登录的用户是不是文章作者
            User currentUser = (User) session.getAttribute(AppConfig.USER_SESSION_KEY);
            if(currentUser.getId() == blog.getUserId()){
                //表示当前登录用户就是作者,设置user的isAuthor为True
                user.setAuthor(true);
            }
            //返回用户作者
            jsonStr = objectMapper.writeValueAsString(AppResult.success(user));
        }
        resp.getWriter().write(jsonStr);
    }
}

postman测试:使用用户名为“小花花”登录,其中blog_id=4是小花花写的博客,blog_id=5是另一个作者写的博客。

情况1:登录是小花花,博客作者也是小花花,说明是作者;

 情况2:登录作者是小花花,博客作者是小叶子,不是同一个 作者,返回false。

(2)blogServlet中实现doDelete方法

 @Override
    protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //(1)设置编码格式
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //(2)获取前端控件参数
        String blogId = req.getParameter("blog_Id");
        //非空校验
        if(StringUtils.isEmpty(blogId)){
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("没有找到文章编号!")));
        }
        //(3)获取当前登录的用户,session中获取
        User user = UserUtils.checkUserLoginStatus(req);
        //表明用户没有登录
        if(user == null){
            String jsonStr = objectMapper.writeValueAsString(AppResult.failed("当前用户没有登录,请登录后再试!"));
            resp.getWriter().write(jsonStr);
            //设置HTTP状态码
            resp.setStatus(403);
            //中断代码流程
            return;
        }
        //(4)判断当前登录的用户与博客作者是否相同:通过登录用户的用户Id与写博客的用户Id做比较
        //首先获取博客对象
        Blog blog = blogDao.selectById(Long.valueOf(blogId));
        //判断博客是否存在
        if(blog == null){
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("文章不存在")));
        }
        //判断是否同一个作者
        if(user.getId() != blog.getUserId()){
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("无权删除别人的文章!")));
            return;
        }
        //(5)否则调用Dao进行删除操作
        int row = blogDao.deleteById(Long.valueOf(blogId));
        //(6)判断结果
        if(row != 1){
            resp.getWriter().write(objectMapper.writeValueAsString(AppResult.failed("删除失败")));
            return;
        }
        //说明删除成功
        resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("删除成功")));
    }

postman测试:

(1)情况1:无权删除文章

 (2)登录用户和博客作者是同一个人,可以删除;

 去数据库查看,对应的文章也已经删除成功。

 

6.3  前端实现:blog_details.html

        根据后端传回来的结果是否成功(成功说明可以删除文章),初始化一个删除按钮并绑定事件 。 

        对应前端代码增加如下:deleteBlog是一个删除方法,封装好之后调用。

在点击详情之后,都会初始化一个删除按钮,但是只有右权限删除的时候,删除按钮才起作用,否则会给出提示无权删除。不论是不是

 重点代码如下:

  //获取作者信息
        $.ajax({
            type:'get',
            url:'user'+location.search,
            //回调函数
            success : function(respData){
                //成功
                if(respData.code == 0){
                    let user = respData.data;
                    //为控件赋值
                    $('#h_details_username').html(user.username);
                    //生成删除按钮
                    let htmlStr = '<a href="javascript:void(0);">删除</a>'
                    //javascript:void(0)表示点击按钮什么事情都不干
                    //将html转为jquery对象并追加到页面上
                    let deleteEl = $(htmlStr);
                    //得到这个按钮的父标签
                    $('.opts').append(deleteEl);
                    //绑定事件
                    deleteEl.click(deleteBlog);
                }else{
                    alert(respData.message);
                }
            },
            error: function(){
                //打印日志
                console.log('访问出现问题');
            },
            statusCode :{
                403 : function(){
                    //强制跳转到登录界面
                    location.assign(blog_login.html);
                }
            }
        });
//删除文章事件
        function deleteBlog(){
            //如果是否定,则直接删除
            if(!confirm("是否删除?")){
                return;
            };
            //否则向后端发送请求,删除文章
            $.ajax({
                type : 'delete',
                url : 'blog' + location.search,
                //回调函数
                success : function(respData){
                //成功
                if(respData.code == 0){
                    //成功,则跳转到详情页
                    location.assign('blog_list.html');
                }else{
                    alert(respData.message);
                }
            },
                error: function(){
                    //打印日志
                    console.log('访问出现问题');
                },
                statusCode :{
                    403 : function(){
                        //强制跳转到登录界面
                        location.assign(blog_login.html);
                    }
                }
            });

🌈7、注销功能

作用:在服务器把用户对应的session销毁。

(1)LogOutServlet:获取session;销毁:session.invalitate();

(2)前端退出按钮a_list_logout:绑定事件。

7.1 Servlet实现后端

@WebServlet("/logout")
public class LogOutServlet extends HttpServlet {
    private ObjectMapper objectMapper = new ObjectMapper();
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setContentType("application/json;charset = utf-8");
        //获取session
        HttpSession session = req.getSession();
        //判断session是否有效
        if(session!=null){
            //销毁session
            session.invalidate();
        }
        resp.getWriter().write(objectMapper.writeValueAsString(AppResult.success("注销成功")));
    }
}

postman测试

7.2 前端实现 blog_list.html

目前只在这个页面实现bblog_list.htmllog_list.html。

// 功能7:为注销按钮绑定事件
        $('#a_list_logout').click(function(){
            //发送ajax请求
            $.ajax({
                type: 'type',
                url : 'logout',
                success : function(respData){
                    //成功
                    if(respData.code == 0){
                        //跳转到登录页面
                        location.assign('nlog_login.html');
                    }else{
                        //失败
                        alert(respData.message);
                    }
                },
                error : function(){
                    console.log('访问出现问题');
                }
            })
        })

第一次做完这个项目,在知识上,主要有以下体会:

  • 一般都和数据库有关联,第一步是分析需求,建表;
  • 创建这个项目主要分为几个包:utils下存放工具类,model中存放各种实体类(这个类的属性和数据库中表的字段一一对应),dao包下实现类与数据库之间的交互,一般是进行数据库的增删改查获取结果供servlet包使用;common包中定义定义全局的配置变量,比如sessionId;还有响应时候的数据格式:以json的形式返回,定义为一个共用的类servlet包下负责从前端获取请求,主要有获取控件的参数,对参数进行校验,然后对前端发送的请求进行相应的处理,一般会调用dao的结果,然后再将响应结果返回给前端;前端一般通过给控件设置选择器(id选择器,类选择器,或者button按钮绑定事件),然后获取控件输入的值封装成数据,对参数进行校验,发送ajax 请求给后端(主要是type,url,data,content-type)然后通过会回调函数获取后端结果做处理。
  • 在前后端交互的过程中,要约定好参数的解析方式。

        行文至此,历时一周,终于完成了这个简单的博客系统项目。虽然做的功能比较简单且单薄,而且掌握的不够充分,但是对于自己来说确实学习到了很多东西。感觉有一种“哦~原来是这样的”恍然大悟,或者说某个时刻的豁然开朗。虽然掌握的内容不多,写的也不是很全面,但是是对自己所学东西的一个记录吧~希望之后回过头来再看,能有更多的启发和思考。


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/728388.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023年开放式蓝牙耳机选购指南!南卡/韶音/Cleer/索尼/飞利浦等开放式耳机怎么选!最热的开放式蓝牙耳机大盘点!

前言 大家好&#xff0c;作为专注耳机研究多年的发烧级爱好者&#xff0c;毫不夸张的说我为耳机花的钱比买衣服还多&#xff0c;很多人都在问我开放式耳机到底有没有必要买&#xff1f;答案毫无疑问是有必要&#xff01;开放式耳机佩戴舒适又安全的特质让它在耳机届风靡&#…

动态SLAM论文(7) — DOT: Dynamic Object Tracking for Visual SLAM

目录 1 Introduction 2 Related work 3 DOT A 系统概述 B. 实例分割 C. 相机和物体跟踪 D. 跟踪质量、异常值和遮挡 E. 目标是否在运动&#xff1f; F. 掩码传播 4 Experimental results 5 Conclusions 摘要 - 本文我们介绍了DOT&#xff08;Dynamic Object Trackin…

计算机网络实验报告——Wireshark 抓包分析

1. Wireshark软件下载 这里放一个蓝奏云下载链接&#xff1a; https://wwix.lanzoue.com/iEklv11klvje 密码:4g0n 2. Wireshark软件抓包使用 将该软件安装之后&#xff0c;双击打开&#xff0c;首先选择你的过滤器方式&#xff0c;我这里电脑连的是WiFi&#xff0c;就选择了这…

分布式搜索--elasticsearch

一、初识 elasticsearch 1. 了解 ES ① elasticsearch 是一款非常强大的开源 搜索引擎&#xff0c;可以帮助我们从海量数据中 快速找到需要的内容 ② elasticsearch 结合 kibana、Logstash、 Beats&#xff0c;也就是 elastic stack (ELK)&#xff0c;被 广泛应用在日志数据分…

【CANoe示例分析】PythonCAPL_Call_Demo

该工程由Vector官方提供,目的是演示Python如何调用CAPL文件里的自定义函数。里面除了CANoe工程文件外,还有python文件和CAPL: 提供了两种CANoe版本的工程文件,选择其中一种打开即可。 首先我们要确定CAPL文件AnalyseFunctions.can在CANoe工程内的什么地方?首先想到的是Si…

Apache Doris 在金融壹账通指标中台的应用实践

本文导读&#xff1a; 金融壹账通作为中国平安集团的联营公司&#xff0c;依托平安集团 30 多年金融行业的丰富经验及自主科研能力&#xff0c;向客户提供“横向一体化、纵向全覆盖”的整合产品&#xff0c;以“技术业务”为独特竞争力&#xff0c;帮助客户提升效率、提升服务…

vuex中的四个map方法的使用

vuex中的四个map方法的使用 vuex里面有四个map方法&#xff0c;他们分别可以针对不同的元素进行不同的代码生成 这四个map方法都是异曲同工&#xff0c;明白了一个基本上都明白了 1 编写案例 现在想要展示一段文本&#xff0c;其中里面两个参数要存在store的state里面&…

怎样优雅地增删查改(二):扩展身份管理模块

文章目录 用户关系管理扩展组织管理功能创建可查询仓储 实现控制器测试接口 身份管理模块&#xff08;Identity模块&#xff09;为通用查询接口的按组织架构查询和按户关系查询提供查询依据。 身份管理模块的领域层依赖Volo.Abp.Identity.Domain Abp为我们实现了一套身份管理模…

CentOS 7镜像下载 以及 DVD ISO 和 Minimal ISO 等各版本的区别介绍

1.官网下载 官网下载地址&#xff1a;官网下载链接 点击进入下载页面&#xff0c;随便选择一个下载即可&#xff08;不推荐&#xff0c;推荐阿里云下载&#xff0c;见下文&#xff09; 阿里云下载站点&#xff08;速度非常快推荐&#xff09; 阿里云下载链接&#xff1a; http…

免费下载!10个3D素材网站推荐

在设计工作中&#xff0c;3D素材可以帮助设计师创建高质量的UI设计&#xff0c;提高设计效率和准确性。本文将为您推荐10个好用的3D素材网站&#xff0c;助力设计师实现高效创作。 1.即时设计资源广场 即时设计资源广场是一个致力于为设计师提供丰富多样的设计资产和灵感的社…

chatgpt 与传统3D建模对比分析

推荐&#xff1a;将NSDT场景编辑器加入你的3D工具链 随着人工智能技术的发展&#xff0c;越来越多的领域正逐渐被AI模型所取代。ChatGPT作为一种自然语言处理技术&#xff0c;越来越为人们所熟悉。最近&#xff0c;一些3D建模领域的专家想知道ChatGPT是否可以取代传统的手动3D建…

ClickHouse主键索引最佳实践

在本文中&#xff0c;我们将深入研究ClickHouse索引。我们将对此进行详细说明和讨论&#xff1a; ClickHouse的索引与传统的关系数据库有何不同ClickHouse是怎样构建和使用主键稀疏索引的ClickHouse索引的最佳实践 您可以选择在自己的机器上执行本文给出的所有Clickhouse SQL…

C++之Clang属性大全(一百五十一)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

NC65自由报表参数设置后,报表发布成节点后,点击查询,在查询框输入条件后,参数值没有拼接到sql中的解决方法

NC65自由报表参数设置后&#xff0c;报表发布成节点后&#xff0c;点击查询&#xff0c;在查询框输入条件后&#xff0c;参数值没有拼接到sql中的解决方法 在语义分析模型中把sql语句放进去&#xff0c;把字段和查询查询设置好 语义模型的sql使用了parameter把参数输入 sel…

eNSP-OSPF组播拓展复杂区域连接

OSPF动态路由 文章目录 OSPF动态路由一、题目要求二、题目分析三、拓扑结构四、基础配置五、测试验证 一、题目要求 1 、 使用172.16.0.0.0/16网段进行子网划分 2 、 用OSPF协议达到全网可达 3 、 保证所有设备均具备最少的 LSDB 以及路由表 二、题目分析 1.网段划分 5个O…

将OxyPlot封装成用户控件后在WPF中的应用

1、文件架构 2、加载依赖项 Newtonsoft.Json OxyPlot.Wpf 3、NotifyBase.cs namespace Accurate.Common {public class NotifyBase : INotifyPropertyChanged{public event PropertyChangedEventHandler? PropertyChanged;public void DoNotify([CallerMemberName] string p…

【深度学习】日常笔记10

loss_fn nn.MSELoss(reductionnone)这行代码&#xff0c;在这个上下文中&#xff0c;loss_fn实际上是一个损失函数对象而不是返回的具体值。 使用nn.MSELoss(reductionnone)创建的loss_fn是一个均方误差损失函数对象&#xff0c;而不是计算后的结果。要计算具体的损失值&…

(02)Cartographer源码无死角解析-(79) ROS服务→子图压缩与服务发送

讲解关于slam一系列文章汇总链接:史上最全slam从零开始&#xff0c;针对于本栏目讲解(02)Cartographer源码无死角解析-链接如下: (02)Cartographer源码无死角解析- (00)目录_最新无死角讲解&#xff1a;https://blog.csdn.net/weixin_43013761/article/details/127350885 文…

一篇文章带你吃透Go语言的Atomic和Channel--实战方法

一篇文章带你吃透Go语言的Atomic和Channel–实战方法 Atomic 要保证原子操作&#xff0c;一定要使用这几种方法 我们在学习 Mutex、RWMutex 等并发原语的实现时&#xff0c;你可以看到&#xff0c;最底层是通过 atomic 包中的一些原子操作来实现的 你可能会说&#xff0c;这些…

C语言中级篇请看另一篇文章,这一篇文章只写给高手看【高阶C语言】【更新中】【原创】

文章目录 前言define和typedef的区别?前言 关于C语言,博主已经写了不少的笔记总结了,C语言基础版可看我的专栏里面的C语言精华,C语言进阶版可看我的谭浩强C语言第五版,C语言高阶版看此篇文章即可 C Primer Plus书籍,C语言精华,截图 + 代码 + 学习总结笔记【11000字】【…