您现在的位置是:首页 > 正文

基于SpringBoot+MyBatis实现的个人博客系统(一)

2024-04-01 06:57:54阅读 1

这篇主要讲解一下如何基于SpringBoot和MyBatis技术实现一个简易的博客系统(前端页面主要是利用CSS,HTML进行布局书写),前端的静态页面代码可以直接复制粘贴,后端的接口以及前端发送的Ajax请求需要自己书写.

博客系统需要完成的接口:

  • 注册
  • 登录
  • 博客列表页展示
  • 博客详情页展示
  • 发布博客
  • 修改博客
  • .......

 完整版代码详见Gitee:blogsystem · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

项目亮点:

  1. 密码实现加盐处理,确保安全性;
  2. Session升级,由原来的内存存储改为通过Redis存储,不会丢失,并且支持分布式部署;
  3. 功能升级,对于博客列表的展示添加了分页功能;
  4. 登录验证升级,添加了拦截器的功能对用于的登录进行校验;
  5. ......

一, 项目的搭建

1,导入依赖坐标(创建SpringBoot项目)

在书写任何一个项目的同时,需要先将项目的基础给搭建好,搭建项目需要提前考虑好项目的一些功能需要哪些依赖,从而进行添加,如何创建SpringBoot项目可以看我的另一篇博客:SpringBoot项目的创建和使用_蜡笔小心眼子!的博客-CSDN博客

 博客系统需要添加的依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.16</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>blogsystem</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>blogsystem</name>
    <description>blogsystem</description>
    <properties>
        <java.version>1.8</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter</artifactId>
            <version>2.3.1</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.mybatis.spring.boot</groupId>
            <artifactId>mybatis-spring-boot-starter-test</artifactId>
            <version>2.3.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

2,书写配置文件

需要使用MyBatis技术的项目需要配置你的数据库相关的信息以及Mapper的xml文件存储的位置,同时也可以在配置文件中定义一下日志的打印级别,从而方便查看数据库操作的完整信息:

#配置数据库连接信息
spring:
  datasource:
    url: "你自己的数据库"
    username: root
    password: "数据库对应的密码"
    driver-class-name: com.mysql.cj.jdbc.Driver

#配置Mapper的xml文件存储信息
mybatis:
#  xml的存储位置
  mapper-locations: classpath:mapper/*Mapper.xml
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

#配置日志打印级别
logging:
  level:
    com:
      example:
        demo: debug

二, 将前端页面部署到项目中(resource文件下的static目录中)

对于后端程序员来说可以不用特别注重前端样式的书写,但是需要看得懂前端的代码以及和后端交互的请求即可(学有余力的情况下可以适当学习从而优化自己项目中的前端页面),这里的静态页面信息可以直接在我的码云中进行下载:SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)博客系统(静态页面).zip · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)SSM配置信息: 存放SSM项目中的一些配置信息 (gitee.com)

将下载好的前端页面全选之后直接复制到static目录下即可:

三, 初始化数据库

在配置文件中我们已经配置了数据库的连接信息,但是此时在数据库中还没有初始化一些数据,所以我们需要初始化数据,方便写项目的时候进行测试,初始化数据库的SQL代码也可以在我们的码云中进行下载:博客系统初始化数据库-ssm.sql · 徐明园/SSM配置信息 - 码云 - 开源中国 (gitee.com)

查看数据库中的表即相应表结构:

 

 四, 对项目的整体架构进行分层

一个企业级的SM项目都需要对其进行合理的分层,每一层处理每一层的业务逻辑,我在项目中的分层如下:

  • common:一些工具类
  • config:配置信息类
  • controller:处理前端请求的类
  • entity:实体类(也可以写成model)
  • mapper:用来和Mapper.xml文件交互的接口类
  • service:处于controller和mapper之间的类

五, 书写前后端交互的功能

从这里开始就是项目的核心了,这里开始可以对前后端的接口和相应功能进行书写了!

1, 统一返回对象的封装

为了给前端返回统一的对象,后端需要定义一个类对返回的数据进行封装,该类包含code,msg和data三个属性,该类定义在common包下:

package com.example.blogsystem.common;

import lombok.Data;

import java.io.Serializable;

/**
 * 统一返回对象
 * 返回成功的话 code 设置成 200
 * 反悔失败的话 code 设置成本身的 code
 */

@Data
public class AjaxResult implements Serializable {
    private int code;
    private String msg;
    private Object data;

    /**
     * 返回成功
     *
     * @param data
     * @return
     */
    public static AjaxResult success(Object data) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(200);
        ajaxResult.setMsg("");
        ajaxResult.setData(data);
        return ajaxResult;
    }

    public static AjaxResult success(Object data, String msg) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(200);
        ajaxResult.setMsg(msg);
        ajaxResult.setData(data);
        return ajaxResult;
    }

    /**
     * 返回失败
     *
     * @param code
     * @param msg
     * @return
     */

    public static AjaxResult fail(Integer code, String msg) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(code);
        ajaxResult.setMsg(msg);
        ajaxResult.setData("");
        return ajaxResult;
    }

    public static AjaxResult fail(Integer code, String msg, String data) {
        AjaxResult ajaxResult = new AjaxResult();
        ajaxResult.setCode(code);
        ajaxResult.setMsg(msg);
        ajaxResult.setData(data);
        return ajaxResult;
    }
}

2, 注册功能

注册功能就是用户给后端发送一次请求之后,后端就会在数据中新增一条用户记录!

前端

<!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/conmmon.css">
    <link rel="stylesheet" href="css/login.css">
    <!-- 引入jquery的js文件 -->
    <script src="js/jquery.min.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="login.html">登陆</a>
    <!-- <a href="#">注销</a> -->
</div>
<!-- 版心 -->
<div class="login-container">
    <!-- 中间的注册框 -->
    <div class="login-dialog">
        <h3>注册</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" id="username">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" id="password">
        </div>
        <div class="row">
            <span>确认密码</span>
            <input type="password" id="password2">
        </div>
        <div class="row">
            <button id="submit" onclick="mysub()">提交</button>
        </div>
    </div>
</div>

<!-- 最好把js的代码写在这下面 因为js代码的代码是按照从上往下执行的 如果写在上面的话 可能js的代码获取的一些标签在下面还没有生效 会获取失败 -->
<script>
    function mysub(){
        // 1.非空判断
        // 1.1 先得到输入的组件
        var username = jQuery("#username");
        var password = jQuery("#password");
        var password2 = jQuery("#password2");
        // 1.2 判断输入组件是否为空
        if(username.val().trim()==""){
            alert("请先输入用户名!");
            username.focus(); // 聚焦光标
            return false;
        }
        if(password.val().trim()==""){
            alert("请先输入密码!");
            password.focus();
            return false;
        }
        if(password2.val().trim()==""){
            alert("请先输入确认密码!");
            password2.focus();
            return false;
        }
        if(password.val()!=password2.val()){
            alert("两次密码输入不一致性,请先检查!");
            password.focus();
            return false;
        }
        // 2.先把提交按钮设置成不可用(禁用)
        jQuery("#submit").attr("disabled","disabled");
        // 3.将当前页面的数据提交给后端
        jQuery.ajax({
            url:"/user/reg",
            type:"POST",
            data:{
                "username":username.val().trim(),
                "password":password.val().trim()
            },
            success:function(res){
                // 4.根据后端返回的结果(成功or失败)再处理后续流程
                if(res.code==200 && res.data==1){
                    alert("注册成功!");
                    location.href = "login.html"; // 调整到登录页面
                }else{
                    alert("抱歉:操作失败!"+res.msg);
                    // 取消禁用
                    jQuery("#submit").removeAttr("disabled");
                }
            }
        });
    }
</script>
</body>

</html>

 前端给后端发送请求都是通过Ajax来实现的,所以对于任何需要发送Ajax请求的页面都需要导入js的依赖(后面就不再赘述)

后端(这里的后端只展示controller包中的代码)

@RequestMapping("/reg")
    public AjaxResult reg(UserInfo userInfo) {
        //1.对前端传递来的参数进行校验
        if (userInfo == null || !StringUtils.hasLength(userInfo.getUsername()) || !StringUtils.hasLength(userInfo.getPassword())) {
            return AjaxResult.fail(-1, "参数有误!");
        }
        //2.与数据库进行交互实现注册的功能
        //将密码进行加盐加密
        userInfo.setPassword(PasswordTools.encrypt(userInfo.getPassword()));
        int result = userService.reg(userInfo);
        //3.对于查询结果给前端进行反馈
        return AjaxResult.success(result);
    }

3, 登录功能 

登录就是前端给后端传递用户名和密码,后端从数据库中查询是否存在这样的用户!

前端

<!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/conmmon.css">
    <link rel="stylesheet" href="css/login.css">
    <script src="js/jquery.min.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="reg.html">注册</a>
</div>
<!-- 版心 -->
<div class="login-container">
    <!-- 中间的登陆框 -->
    <div class="login-dialog">
        <h3>登陆</h3>
        <div class="row">
            <span>用户名</span>
            <input type="text" id="username">
        </div>
        <div class="row">
            <span>密码</span>
            <input type="password" id="password">
        </div>
        <div class="row">
            <button id="submit" onclick="mysub()">提交</button>
        </div>
    </div>
</div>

<script>
    function mysub() {
        //1.对提交的数据进行判空操作
        var username = jQuery("#username");
        var password = jQuery("#password");
        if(username.val().trim() == "") {
            alert("请输入用户名!");
            username.focus();
            return false;
        }
        if(password.val().trim() == "") {
            alert("请输入密码!");
            password.focus();
            return false;
        }

        //2.发送数据给服务器
        jQuery.ajax({
            url:"user/login",
            type:"post",
            data:{
                "username":username.val().trim(),
                "password":password.val().trim()
            },
            success:function(res) {
                //这里针对服务器的响应数据 规定返回1是成功 返回0是失败
                if(res.code == 200 && res.data == 1) {
                    alert("恭喜:登录成功!");
                    location.href = "myblog_list.html";
                } else {
                    alert("抱歉:登录失败!" + res.msg);
                    return false;
                }
            }
        });
    }
</script>
</body>

</html>

登录功能的前端代码和注册功能前端代码几乎一模一样

后端

@RequestMapping("/login")
    public AjaxResult login(String username, String password, HttpServletRequest request) {
        //1.对前端传递来的参数进行校验
        if (!StringUtils.hasLength(username) || !StringUtils.hasLength(password)) {
            return AjaxResult.fail(-1, "参数有误!");
        }

        //2.根据用户名去数据库中进行查询
        UserInfo userInfo = userService.login(username);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-2, "用户名或者密码错误!");
        }

        //对数据库中查找的密码进行解密
//        if (!PasswordTools.check(password,userInfo.getPassword())) {
//            return AjaxResult.fail(-2, "用户名或者密码错误!");
//        }
        
        if (!userInfo.getPassword().equals(password)) {
            return AjaxResult.fail(-2, "用户名或者密码错误!");
        }

        //当前表示登陆成功 需要存储session
        HttpSession session = request.getSession();
        session.setAttribute(ApplicationVariable.USERINFO_SESSION_KEY, userInfo);
        return AjaxResult.success(1);
    }

登录的时候需要存储用户的session(会话)信息,因为session的Key需要在多个地方使用,我们将该属性抽象出来放在了common这个公共包下了:

package com.example.blogsystem.common;

public class ApplicationVariable {

    public static final String USERINFO_SESSION_KEY = "USERINFO_SESSION_KEY";
}

 对于取出session()会话)中的的用户信息也可以将其封装公共的类,放在common包下:

package com.example.blogsystem.common;

import com.example.blogsystem.entity.UserInfo;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

public class UserSessionTools {
    public static UserInfo getLoginUser(HttpServletRequest request) {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {
            return (UserInfo) session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY);
        }

        return null;
    }
}

4, 实现拦截器

对于一些博客信息的操作需要用户进行登录,所以可以通过拦截器判断用户具有相应的权限,只有通过拦截器的用户才可以操作,可以将拦截的配置信息放在config包下.

实现HandlerInterceptor接口:

package com.example.blogsystem.config;

import com.example.blogsystem.common.ApplicationVariable;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;


@Configuration
public class LoginInterceptor implements HandlerInterceptor {


    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession(false);
        if (session != null && session.getAttribute(ApplicationVariable.USERINFO_SESSION_KEY) != null) {
            return true;
        }

        response.sendRedirect("/login.html");//没有通过拦截器的请求需要跳转到登录页面先登录
        return false;
    }
}

实现WebMvcConfigurer进行配置接口:

package com.example.blogsystem.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;

@Configuration
public class MyConfig implements WebMvcConfigurer {

    @Resource
    private LoginInterceptor loginInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/**") //拦截所有的url
                .excludePathPatterns("/login.html")
                .excludePathPatterns("/reg.html")
                .excludePathPatterns("/blog_list.html")
                .excludePathPatterns("/blog_content.html")
                .excludePathPatterns("/css/**")
                .excludePathPatterns("/editor.md/**")
                .excludePathPatterns("/img/**")
                .excludePathPatterns("/js/**")
                .excludePathPatterns("/user/reg")
                .excludePathPatterns("/user/login");
    }
}

这里需要先放开所有的静态页面,图片以及登录和注册接口.

5, 博客添加功能

博客添加功能就是前端向后端提交博客的一些信息,后端在文章表中插入一条文章记录即可!

前端

<!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/conmmon.css">
    <link rel="stylesheet" href="css/blog_edit.css">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css" />
    <script src="js/jquery.min.js"></script>
    <script src="editor.md/editormd.js"></script>
</head>

<body>
    <!-- 导航栏 -->
    <div class="nav">
        <img src="img/logo2.jpg" alt="">
        <span class="title">我的博客系统</span>
        <!-- 用来占据中间位置 -->
        <span class="spacer"></span>
        <a href="blog_list.html">主页</a>
        <a href="#">注销</a>
    </div>
    <!-- 编辑框容器 -->
    <div class="blog-edit-container">
        <!-- 标题编辑区 -->
        <div class="title">
            <input id="title" type="text" placeholder="在这里写下文章标题">
            <button onclick="mysub()">发布文章</button>
        </div>
        <!-- 创建编辑器标签 -->
        <div id="editorDiv">
            <textarea id="editor-markdown" style="display:none;"></textarea>
        </div>
    </div>

    <script>
        var editor;
        function initEdit(md){
            // 编辑器设置
            editor = editormd("editorDiv", {
                // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉. 
                width: "100%",
                // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
                height: "calc(100% - 50px)",
                // 编辑器中的初始内容
                markdown: md,
                // 指定 editor.md 依赖的插件路径
                path: "editor.md/lib/",
                saveHTMLToTextarea: true // 
            });
        }
        initEdit("# 在这里写下一篇博客"); // 初始化编译器的值
        // 提交
        function mysub(){
            //1.对文章标题和内容进行判空操作
            var title = jQuery("#title");
            var content = editor.getValue();
            if(title.val().trim() == "") {
                alert("请输入文章标题!");
                title.focus();
                return false;
            }
            if(content == "") {
                alert("请输入正文!")
                return false;
            }
            //2.提交数据给后端
            jQuery.ajax({
                url:"/art/add",
                type:"post",
                data:{
                    "title":title.val(),
                    "content":content
                },
                success:function (res) {
                    //假设文章添加成功后端给前端返回的data中的数据是1
                    if(res.code == 200 && res.data == 1) {
                        alert("恭喜:文章添加成功!");
                        if (confirm("是否继续添加文章?")) {
                            //如果继续继续添加文章的话 需要刷新此页面
                            location.href = location.href;
                        } else {
                            //不继续添加文章需要跳转到文章列表页
                            location.href = "myblog_list.html";
                        }
                    } else{
                        alert("抱歉:文章添加失败!" + res.msg);
                    }
                }
            })
            // alert(editor.getValue()); // 获取值
            // editor.setValue("#123") // 设置值
        }
    </script>
</body>

</html>

这里的前端页面引入了MarkDown编辑器,所以添加博客的时候相较于其他官方博客系统更加真实,而且该编辑器提供了一些Api让我们进行格式转换的时候更加方便.

//相关API
alert(editor.getValue()); // 获取值
editor.setValue("#123") // 设置值

后端

@RequestMapping("/add")
    public AjaxResult add(ArticleInfo articleInfo, HttpServletRequest request) {
        //1.对前端传递来的参数进行判空操作
        if (articleInfo == null || !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //2.获取当前的uid进行校验
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //3.封装uid进行持久化
        articleInfo.setUid(userInfo.getId());
        int result = articleService.add(articleInfo);
        //4.给前端进行数据反馈
        return AjaxResult.success(result);
    }

这里数据库存储的是MarkDown格式的数据,是为了方便进行修改的时候直接进行修改省去了一次从Html转换成MarkDown格式的操作.

6, 博客编辑功能

博客编辑功能需要实现两个操作:

1.先去查询当前文章的信息进行展示(页面加载的时候进行调用)

2.提交修改操作(触发提交按钮的时候进行调用)

前端

<!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/conmmon.css">
    <link rel="stylesheet" href="css/blog_edit.css">

    <!-- 引入 editor.md 的依赖 -->
    <link rel="stylesheet" href="editor.md/css/editormd.min.css"/>
    <script src="js/jquery.min.js"></script>
    <script src="editor.md/editormd.js"></script>
    <script src="js/urltools.js"></script>
    <script src="js/logout.js"></script>
</head>

<body>
<!-- 导航栏 -->
<div class="nav">
    <img src="img/logo2.jpg" alt="">
    <span class="title">我的博客系统</span>
    <!-- 用来占据中间位置 -->
    <span class="spacer"></span>
    <a href="blog_list.html">主页</a>
    <a href="javascript:logout()">注销</a>
</div>
<!-- 编辑框容器 -->
<div class="blog-edit-container">
    <!-- 标题编辑区 -->
    <div class="title">
        <input id="title" type="text" placeholder="在这里写下文章标题">
        <button onclick="mysub()">发布文章</button>
    </div>
    <!-- 创建编辑器标签 -->
    <div id="editorDiv">
        <textarea id="editor-markdown" style="display:none;"></textarea>
    </div>
</div>

<script>
    var isSubmit = 1;
    var id = 0;
    var editor;

    function initEdit(md) {
        // 编辑器设置
        editor = editormd("editorDiv", {
            // 这里的尺寸必须在这里设置. 设置样式会被 editormd 自动覆盖掉.
            width: "100%",
            // 高度 100% 意思是和父元素一样高. 要在父元素的基础上去掉标题编辑区的高度
            height: "calc(100% - 50px)",
            // 编辑器中的初始内容
            markdown: md,
            // 指定 editor.md 依赖的插件路径
            path: "editor.md/lib/",
            saveHTMLToTextarea: true //
        });
    }

    // initEdit("# 在这里写下一篇博客"); // 初始化编译器的值
    //接口1:先去查询当前文章的信息进行展示 页面加载的时候进行调用
    function initArt() {
        //1.通过queryString获取文章id
        id = getParamByKey("id");
        if (id == null || id <= 0) {
            isSubmit = 0;
            alert("抱歉:非法参数!");
            return false;
        }
        //2.向后端发送请求 获取对应id的文章并展现在前端页面
        jQuery.ajax({
            url: "/art/getdetailbyid",
            type: "post",
            data: {
                "id": id
            },
            success: function (res) {
                if (res.code == 200 && res.data != null && res.data.id > 0) {
                    //文章查询成功
                    jQuery("#title").val(res.data.title);
                    initEdit(res.data.content);
                } else {
                    //文章获取失败
                    isSubmit = 0;
                    alert("抱歉:非法参数!" + res.msg);
                }
            }
        });
    }

    initArt();

    //接口2:提交修改操作 触发提交按钮的时候进行调用
    // 提交
    function mysub() {
        if (isSubmit == 0) {
            alert("抱歉:非法操作,请刷新页面再试!");
            return false;
        }
        //1.非空判断
        var title = jQuery("#title");
        var content = editor.getValue();
        if (title.val().trim() == "") {
            alert("请输入文章标题!");
            title.focus();
            return false;
        }
        if (content == "") {
            alert("请输入正文!")
            return false;
        }

        //2.提交请求给后端
        jQuery.ajax({
            url: "/art/update",
            type: "post",
            data: {
                "id": id,
                "title": title.val(),
                "content": content
            },
            success: function (res) {
                //规定修改成功后端返回1
                if (res.code == 200 && res.data == 1) {
                    alert("恭喜:修改成功!");
                    location.href = "myblog_list.html";
                } else {
                    alert("抱歉:非法参数!" + res.msg);
                }
            }
        });
        // alert(editor.getValue()); // 获取值
        // editor.setValue("#123") // 设置值
    }
</script>
</body>

</html>

后端

/**
     * 对文章进行修改的时候也需要对拿到文章进行权限验证 拿到的文章的uid必须和登录的用户的id一致
     * 防止登录的用户对其他人的文章进行篡改
     */

    @RequestMapping("/getdetailbyid")
    public AjaxResult getdetailbyid(Integer id, HttpServletRequest request) {
        //1.对id进行判空操作
        if (id == null || id <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }

        //2.获取到登录用户的id
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }

        //3.封装id和uid进行持久化操作
        return AjaxResult.success(articleService.getDetailByIdAndUid(id, userInfo.getId()));
    }

    @RequestMapping("/update")
    public AjaxResult update(ArticleInfo articleInfo, HttpServletRequest request) {
        //1.对前端传递的参数进行判空
        if (articleInfo == null || articleInfo.getId() <= 0
                || !StringUtils.hasLength(articleInfo.getTitle()) || !StringUtils.hasLength(articleInfo.getContent())) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        //2.获取uid进行封装并进行持久化
        UserInfo userInfo = UserSessionTools.getLoginUser(request);
        if (userInfo == null || userInfo.getId() <= 0) {
            return AjaxResult.fail(-1, "参数错误!");
        }
        articleInfo.setUid(userInfo.getId());
        articleInfo.setUpdatetime(LocalDateTime.now());
        //3.给前端返回数据
        int result = articleService.update(articleInfo);
        return AjaxResult.success(result);
    }

根据文章id进行查找文章时,必须要进行校验,确保查询到的文章时该登录用户的文章,即文章表中的uid = 用户表中的id.

网站文章

  • 【vue2】解决Vuex刷新页面数据丢失的问题

    【vue2】解决Vuex刷新页面数据丢失的问题

    解决Vuex刷新页面数据丢失的问题

    2024-04-01 06:57:50
  • 新东方高中计算机模拟试卷,同等学力计算机综合模拟试题(2)

    1. 比较下列集合的基数大小并给出证明:A×A,P(A),2→A,A→2.解答与评分标准:|A×A| = |2→A| = |A|2(2 分),|P(A)| = |A→2| = 2|A|(2 分)。分情况讨论:(1) A 为空集:注意A→2={空关系},|A×A| = |2→A| = 0 < |P(A)| = |A→2| = 1。(1 分)(2) A 为有限集且|A|=1:|A×A| = |2...

    2024-04-01 06:57:40
  • Ubuntu下安装pycharm并激活

    Ubuntu下安装pycharm并激活

    1.在官网下载:2.提取到此处3.在含有pycharm.sh的文件夹下,右键,选择在此处打开终端输入./pycharm.sh执行安装好后,看到如下界面:学生可以选择用学生邮箱激活激活方式如下:1)按 buy pycharm 按钮,选择special offers(或进入网址https://www.jetbrains.com/pycharm/buy/?fr...

    2024-04-01 06:56:57
  • 写一程序,用scanf函数输入x,输出y值。

    写一程序,用scanf函数输入x,输出y值。

    有一函数: y=x²+2x-6  (x<0,x≠-3) y=x²-5x+6  (0≤x<10,x≠2,x≠3) y=x²-x-15  (x=-3,x=2,x=3,x≧10) 解题思路:先用scanf函...

    2024-04-01 06:56:51
  • 自己挖坑自己跳 之JsonMappingException: (was java.lang.NullPointerException) (through reference chain:)...

      在Web项目中,我们经常会设计一些与界面相对应的JavaBean作为Entity,而为了兼容前台传入的空值,有些字段我们会用包装类型而不是基本类型。可是往往我的Entity已经设计完成,很多时候我...

    2024-04-01 06:56:43
  • Black Hat Europe 2021议题解读:Wi-Fi Mesh中的安全攻击面

    Black Hat Europe 2021议题解读:Wi-Fi Mesh中的安全攻击面

    近年来,随着万物互联技术的发展,Mesh技术逐渐兴起,Mesh技术是一种组网技术,可将多个接入点组成同一个网络来提供服务,相比于传统的WiFi组网技术,Mesh组网更稳定,更快,扩展性更强...

    2024-04-01 06:56:02
  • JAVA大数类 BigInteger和BigDecimal用法

    BigInteger四则运算 import java.math.BigInteger; import java.util.Scanner; public class Main { public static void main(String[] args) { BigInteger num1, num2; Scanner in = new Scanner(S

    2024-04-01 06:55:55
  • 基于ARMA差分还原的客流量时间序列预测 完整代码数据

    基于ARMA差分还原的客流量时间序列预测 完整代码数据

    基于ARMA差分还原的客流量时间序列预测 完整代码数据

    2024-04-01 06:55:47
  • vuex 模块化

    vuex 模块化

    一般当项目比较大时,如果vuex 的modules模块过多,此时需要用到模块化来管理仓库。如果在index里引入modules中所有的文件,需在index.js中配置以下。比如在addController.js中定义一个变量。页面中通过getter取state属性。在getters.js中获取该属性。页面中存state属性。

    2024-04-01 06:55:08
  • tar打包命令(linux)

    1.打包命令: tar -cvf 归档路径 被打包文件路径。 (c--create archive v-verbose f --file ,f指归档路径,故f必须放在其他选项之后,而且-可省略不写) ...

    2024-04-01 06:55:02