【博客系统】博客系统第八弹:实现用户退出功能、发布博客功能

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


实现用户退出接口


实现原理


前端直接清除掉 token 即可。

image-20250524103916313


删除 userToken 后,刷新页面,会自动跳转到登录页面:

image-20250524104018245


点击注销按钮,前端直接调用删除lovalStorage中的userToken方法,即可实现注销功能:

image-20250524104057439


实现客户端代码


image-20250524104321746

blog_list.htmlblog_detail.html 中,都有注销按钮,点击注销按钮,对应的标签都会触发onclick = logout() 事件:

    <div class="nav">
        <img src="pic/logo2.jpg" alt="">
        <span class="blog-title">我的博客系统</span>
        <div class="space"></div>
        <a class="nav-span" href="blog_list.html">主页</a>
        <a class="nav-span" href="blog_edit.html">写博客</a>
        <a class="nav-span" href="#" onclick="logout()">注销</a>
    </div>

因为在 blog_list.htmlblog_detail.html 中都有注销触发 onclick="logout()"的机制,所以将 logout() 提取到 common.js 中,并在common.js 中完善 logout 方法:

image-20250524104812867

function logout() {
    // 删除 localStorage 中的登录用户 id
    localStorage.removeItem("loginUserId");
    
    // 删除 localStorage 中的 token
    localStorage.removeItem("userToken");
    
    // 跳转到登录页面
    location.href = "blog_login.html";
}

接口测试


重新运行程序,打开 blog_list.html,点击注销:

image-20250524105325163


localStorage 被清除,跳转到登录页面:

image-20250524110840362


重新登录,点击查看全文按钮,进入 blog_detail.html 页面,点击注销:

image-20250524110950065


localStorage 再次被清除,同时跳转到登录页面:

image-20250524111014848


实现发布博客接口


点击写博客按钮,会跳转到 markdown 编辑器

image-20250524112311433


markdown 编辑器并不是我们自己写的,而是使用第三方插件,我们要实现点击发布文章后的逻辑:

image-20250524113031706

我们写好一篇博客后,点击发布文章,后端会将该文章存储到我们 mysql 对应的数据库中,对数据进行持久化;


约定前后端交互接口


  • [请求]

    /blog/add
    
  • [参数]

    {
      "userId": 1,
      "title": "标题",
      "content": "正文"
    }
    
  • [响应]

    {
      "code": 200,
      "msg": "",
      "data": true
    }
    
    • true 表示成功
    • false 表示失败

实现服务器代码


定义实体类

image-20250524114102075

@Data
public class AddBlogRequest {
    private Integer userId;
    private String title;
    private String content;
}

修改 BlogController,新增 add 方法

image-20250524113416362

@Slf4j
@RequestMapping("/blog")
@RestController
public class BlogController {

    @Resource(name = "blogServiceImpl")
    private BlogService blogService;

    @RequestMapping("/add")
    public Boolean addBlog(@RequestBody @Validated AddBlogRequest addBlogRequest){
        // (1) 因为 AddBlogRequest 是 JSON 字符串, 所以需要使用 @RequestBody 来识别
        // (2) 加上 @Validated 注解,方便在 AddBlogRequest 中对属性使用 @NotNull 等注解进行校验
        
        log.info("发布博客, userId: {}, title: {}", addBlogRequest.getUserId(),addBlogRequest.getTitle());
        return blogService.addBlog(addBlogRequest);
    }
}

在Controller中,对参数使用@Validated注解,进而对AddBlogRequest的属性添加@NotNull@NotBlank等注解,以此实现校验功能:

@Data
public class AddBlogRequest {
    @NotNull(message = "userId 不能为空")
    private Integer userId;
    @NotBlank(message = "标题不能为空")
    private String title;
    @NotBlank(message = "内容不能为空")
    private String content;
}

BlogService

image-20250524121053613

public interface BlogService {

    List<BlogInfoReponse> getList();

    BlogInfoReponse getBlogDetail(Integer blogId);

    BlogInfo getBlogInfo(Integer blogId);

    Boolean addBlog(AddBlogRequest addBlogRequest);
}

BlogServiceImpl

image-20250524121141470

@Service
@Slf4j
public class BlogServiceImpl implements BlogService {

    @Autowired
    private BlogInfoMapper blogInfoMapper;

    @Override
    public Boolean addBlog(AddBlogRequest addBlogRequest) {
        BlogInfo blogInfo = new BlogInfo();
        BeanUtils.copyProperties(addBlogRequest, blogInfo);
        try{
            Integer result = blogInfoMapper.insert(blogInfo);   
            // (1) INSERT INTO 表名 (列1, 列2, 列3) VALUES (值1, 值2, 值3);
            
            if(result == 1){
                // (2) 上面的 sql 语句会返回受影响行数, 返回结果为 1, 表示成功插入一行数据(一篇博客)到博客数据表中
                return true;
            }
            return false;
        }catch (Exception e){
            log.error("博客输入失败, e", e);
            throw new BlogException("内部错误, 请联系管理员"); 
            // (3) 为了实现统一的结果返回, 我们需要抛出一个异常, 但不直接使用 e.getMessage(), 因为后端的错误信息不应直接暴露给前端 
        }
    }
}

image-20250524121910928


测试接口


重新运行程序,测试添加博客接口:

image-20250524123807930


检查数据库:

image-20250524123949576


Editor.md


Editor.md 是一个开源的页面 Markdown 编辑器组件。

使用示例

<link rel="stylesheet" href="editormd/css/editormd.css" />
<div id="test-editor">
    <textarea style="display:none;">### 关于 Editor.md

**Editor.md** 是一款开源的、可嵌入的 Markdown 在线编辑器(组件),基于 CodeMirror、jQuery 和 Marked 构建。
    </textarea>
</div>
<script src="https://cdn.bootcss.com/jquery/1.11.3/jquery.min.js"></script>
<script src="editormd/editormd.min.js"></script>
<script type="text/javascript">
    $(function() {
        var editor = editormd("test-editor", {
            // width : "100%",
            // height : "100%",
            path : "editormd/lib/"
        });
    });
</script>
  • 使用时引入对应依赖即可
  • "test-editor" 为 Markdown 编辑器所在的 div 的 id 名称
  • path 为 editor.md 依赖所在的路径

实现客户端代码


修改 blog_edit.html

image-20250524130623470

<div class="content-edit">
    <div class="push">
        <input type="text" name="" id="title">
        <input type="button" value="发布文章" id="submit" onclick="submit()">
    </div>
    <!-- markdown 插件 html代码 -->
    <div id="editor">
        <textarea style="display:none;" id="content" name="content">##在这里写下一篇博客</textarea>
    </div>
</div>

点击发布文章按钮,会执行 submit() 方法,接下来,我们来完善一下这个方法;


完善 submit 方法:

image-20250524130623470


我们根据下面的 URL、请求 ( JSON 格式 )、返回响应,来实现 submit() 方法中的 ajax 请求:

image-20250524131205275

contentTypefunction submit() {
    $.ajax({
        type : "post",
        url : "/blog/add",
        // (1) 声明请求内容的类型是 JSON 格式
        content : "application/json",

        // (2) stringify() 将 JavaScript 对象或值转换为 JSON 格式的字符串
        data : JSON.stringify({
            userId : localStorage.getItem("loginUserId"),
            // (3) 可以从 localStorage 获取当前登录用户的 id, 也就是这篇博客的作者对应的 id

            title : $("#title").val(),
            contentType : $("#content").val()
            // (4) title、content 从该页面的输入框中拿到输入的值
        }),

        success : function (result){
            if(result.code == "SUCCESS" && result.data == true){
                location.href = "blog_list.html";
            }else{
                alert("博客发布失败");
            }
        }
    });
}

部署程序,验证效果。


测试接口


image-20250524132812890


点击发布按钮,跳转页面:

image-20250524133035046


点击查看全文:

image-20250524133055062


修改详情页显示


此时我们还发现一个问题,那就是博客发布后,内容并没有对特殊符号进行处理:

image-20250524133254123

这个博客列表显示的内容,就需要后端截断一些内容,再显示到前端了,该功能后续再做;


标签博客详情中,也没有把 markdown 语法渲染:

image-20250524133308959

我们需要把markdown语法转为 HTML 语法;


我们先来实现博客详情页中markdownhtml 的功能;

image-20250524134437700


首先,找到 Editor.md 官网的 markdown 转 HTML 页面

HTML Preview(markdown to html) - Editor.md examples

image-20250524134106937


进入开发者模式,查看前端代码:

image-20250524134318013


使用 editormd.markdownToHTML 方法,修改博客正文内容的显示,将 Markdown 内容转换为 HTML

image-20250524134901841

getBlogDetail();
function getBlogDetail(){
    $.ajax({
        type: "get",
        url: "/blog/getBlogDetail" + location.search,
        success: function (result){
            if(result.code == "FAIL"){
                alert(result.errMsg);
                return;
            }

            if(result.code == "SUCCESS" && result.data != null){
                $(".content .title").text(result.data.title);
                $(".content .date").text(result.data.createTime);

                // $(".content .detail").text(result.data.content);  (1) 先注掉这个代码

                // (2) 将数据库中的 content 先从 markdown 转为 html, 再把转好的 html 内容显示到前端页面中

                editormd.markdownToHTML("test-editormd-view", {
                    // (3) "test-editormd-view" 表示转化的 HTML 内容需要放置到哪里, 对应原来的 .content .detail
                    // (4) 注意, "test-editormd-view"  一个 id , 所以我们在 .content .detail 标签中多加一个 id
                    markdown : markdown ,
                });
            }
        }
    });
}

根据上面的注解 (4) ,修改 HTML 部分,将博客正文的 div 标签改为 <div id="detail">,并添加样式:

image-20250524140220535

getBlogDetail();
function getBlogDetail(){
    $.ajax({
        type: "get",
        url: "/blog/getBlogDetail" + location.search,
        success: function (result){
            if(result.code == "FAIL"){
                alert(result.errMsg);
                return;
            }

            if(result.code == "SUCCESS" && result.data != null){
                $(".content .title").text(result.data.title);
                $(".content .date").text(result.data.createTime);
                editormd.markdownToHTML("detail", {
                    // (1) test-editormd-view 改为 detail
                    markdown : result.data.content ,
                    // (2) 右边的 markdown 改为 result.data.content
                });

            }
        }
    });
}

部署程序,观察效果


测试接口


image-20250524140311398


在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值