Java搭配H5实现前后端交互评论功能

发布于:2024-04-16 ⋅ 阅读:(20) ⋅ 点赞:(0)

前言

        最近有个朋友有偿让我帮他们将这个评论组件整合到他们的静态网页当中,并实现数据持久化。后来他觉得太麻烦,就放弃了。尽管没有拿到相应的报酬,但只是花了短短的时间写完了这个简单功能,并有机会将其分享给大家。内容不长,全是干货,请择需浏览。

一、框架介绍

        (1)后端采用 JPA + SpringWeb。这里选择 JPA 主要是因为 JPA 可以自动建表。

        (2)前端采用 H5 + JS。选择 H5 和原生 JS 主要是考虑到灵活性和兼容性。

        (3)源代码已上传到资源绑定,请按需下载

二、前端

        (一)、存在 XSS 漏洞的版本

                1、这个版本的代码,可以看这篇博文。与解决XSS漏洞版本不同的是,这个版本的样式在动态渲染时使用的是 innerHTML 赋值,这种赋值会有一个弊端,就是会对输入内容进行标签解析,如果用户输入了类似“<div>我是文本</div>”的内容,会被解析成H5标签,这将给不法分子攻击并窃取你网站信息制造良机,因此对于用户输入的内容,要做到敏感信息过滤并加以限制或对用户输入的内容采用文本解析的方式。以下是一个正面例子:

const commentContent = document.createElement('div');
commentContent.style.display = 'block';
commentContent.style.marginTop = '8px';
commentContent.classList.add('comment');
commentContent.textContent = value.comment;

               2、该版本没有实现与后端交互,因此内容不会被持久化。

        (二)、解决了 XSS 漏洞的版本

              1、如何请求后端。下面会给出一段 JavaScript 中不使用 Ajax 请求后端的代码示例

    function createOrGetUserName() {
        let userName = localStorage.getItem('userName');
        if (!userName) {
            const url = "http://127.0.0.1:8080/comment/getRandomUserName";
            const req = new XMLHttpRequest();
            // open 函数第三个参数是 async, 是否同步
            req.open('get', url, true);
            // 根据请求后端的参数类型,设置请求头
            req.setRequestHeader('Content-Type', 'application/json')
            req.onreadystatechange = function () {
                if (req.readyState === 4) {
                    if (req.status === 200 || req.status === 304) {
                        const resp = JSON.parse(req.responseText);
                        if (resp.code === 200) {
                            localStorage.setItem('userName', resp.result);
                        }
                    }
                }
            }
            req.send();
        }
        return localStorage.getItem('userName');
    }

              2、请求后端我们一般都会使用 Ajax 或者 VUE 的 Axios 等等,这里可以使用  XMLHttpRequest 来替代,并设计“增删改查”相应的API。

三、后端

          后端这里只是对一些功能做简单介绍。具体代码可以下载源码自行研究。

        (一)、JPA 框架

            1、JPA自动建表。在properties文件中使用如下配置可以在实体(Entity)结构更新时,更新数据库中对应的表结构。

spring.jpa.hibernate.ddl-auto=update

            2、建表前,我们可以创建对应的实体,设计好表的字段与表名。实体创建好后,启动项目,当该实体被应用时,会自动在指定的数据库创建对应的表结构。

@Entity
@Table(name = "ATable")
public class TableName {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    @Column(name = "field_name")
    private String fieldName;

    // ... Too many fields
}

         3、JPA的功能很丰富,比如一对多的查询,可以使用 @oneToMany 注解来实现。自定义 SQL 查询可以在 @Query 注解中完成。这里就不多做介绍了,具体可以去看看相关的博文介绍。

      (二)、评论树的结构

         1、XSS 版本中已经介绍了评论树的结构怎样呈现最合宜。两层关系既直观又容易实现与维护。评论谁?我们会创建一个 Author 字段,来存储被评论者和被回复者的 ID。那么在构建这个3层的 N 叉树时,除去 root,对于每个直接对文章评论的用户放入一层列表,然后对这第一层列表进行递归,并将其所有一级或二级子节点放入这一层列表相应节点的 children 列表即可。下面是构建评论树的 Java 代码。

    public static List<Comment> treeify(List<Comment> res) {
        List<Comment> newRes = new ArrayList<>();
        // 构建关系表
        Map<Integer, List<Comment>> relations = new HashMap<>();
        for (Comment c : res) {
            c.setUuidList(UUIDUtil.getUUIDList(5));
            String admirers = c.getAdmirers();
            Map<String, Boolean> admirerStatus = new HashMap<>();
            if (admirers != null && !admirers.isEmpty()) {
                String[] split = admirers.split(",");

                for (String name : split)
                    admirerStatus.put(name, true);
            }

            c.setAdmirerStatus(admirerStatus);
            // -1 是代表文章作者也就是 N 叉树的 root
            if (-1 == c.getAuthorId())
                newRes.add(c);
       
            List<Comment> commentList = relations.getOrDefault(c.getAuthorId(), new ArrayList<>());
            commentList.add(c);
            // 保存关系
            relations.put(c.getAuthorId(), commentList);
        }
        for (Comment c : newRes) {
            c.setReply(new ArrayList<>());
            // 递归添加子节点
            recur(relations, c.getId(), c);
        }
        return newRes;
    }

    public static void recur(Map<Integer, List<Comment>> rel, Integer id, Comment father) {
        if (rel.containsKey(id)) {
            for (Comment c : rel.get(id)) {
                father.getReply().add(c);
                recur(rel, c.getId(), father);
            }
        }
    }