ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

React全Hook项目实战在线聊天室历程(完结):删帖功能

2021-12-04 23:32:17  阅读:133  来源: 互联网

标签:console log no res ...... React Hook token 删帖


前情提要:

React全Hook项目实战在线聊天室历程(一):基本功能
React全Hook项目实战在线聊天室历程(二):引用与话题功能
React全Hook项目实战在线聊天室历程(三):加个音乐直播?

管理员的权限

只要是在线提供服务的网站一定会有管理员的存在,那我们也给聊天室加一个管理员,不过简单一些,这个管理员只有删帖的功能。

实现思路

为了简单,直接使用SubmitContainer来作为登录窗口,后端根据“tag”属性是否等于特定字符串,来判断前端发来的消息是否是登录的账号密码。登录成功后,后端生产token,前端保存,使用删帖操作时需要带上token,否则无法执行删帖操作。

后端

后端引入jsonwebtoken

npm install jsonwebtoken

修改数据库message表,增加一个字段deleted 默认值为0,被删除时值为1。
修改db.js

// 新增一个删除操作
function deleteMessage(no, callback) {
    let sql = "UPDATE message SET deleted=1 where no=?"
    try {
        conn.query(sql, [no], (err, res) => {
            if (err) {
                // console.log('-----------error---------');
                // console.error(err);
                callback(undefined);
            } else {
                // console.log('------------success------------');
                // console.log(res);
                callback(res);
            }
        })
    } catch (error) {

    }
}
/* ...... */
// 修改方法,被删除的方法不展示给用户
function getMessage(callback) {
    let sql = "select * from ( SELECT * FROM message where deleted=0 order by no desc limit 50) as tmp order by no ;"
    try {
        conn.query(sql, (err, res) => {
            if (err) {
                // console.log('-----------error---------');
                // console.error(err);
                callback(undefined);
            } else {
                // console.log('------------success------------');
                // console.log(res);
                callback(res);
            }
        })
    } catch (error) {

    }
}
// 同理
function getMessageByNo(no, callback) {
    let sql = "select * from message where no=? and deleted != 1;"
    try {
        conn.query(sql, [no], (err, res) => {
            if (err) {
                // console.log('-----------error---------');
                // console.error(err);
                callback(undefined);
            } else {
                // console.log('------------success------------');
                // console.log(res);
                callback(res);
            }
        })
    } catch (error) {

    }
}

修改ws.js

/* ...... */
// 引入jsonwebtoken
var jwt = require('jsonwebtoken');
/* ...... */
// jsonwebtoken加密需要用到的密钥
const secret = "hello"
/* ...... */
// 删除POST方法
router.post("/deleteMsg", (req, res) => {
    const token = req.headers.token
    // 请求头的token如果校验不通过
    // 会抛出错误需要捕获
    try {
        var decode = jwt.verify(token, secret)
        db.deleteMessage(req.body.no, (result) => {
            res.end(JSON.stringify({
                success: true,
                code: 200,
                msg: 'success'
            }))
            // 广播被删除的No号
            bc(clientList, JSON.stringify({
                type: 'delete',
                no: req.body.no
            }))
        })
    } catch (error) {
        console.log("解读出错", token, error)
        res.end(JSON.stringify({
            success: false,
            code: 401,
            msg: 'Token 无效'
        }))
    }
})
/* ...... */
router.post("/post", (req, res) => {
    /* ...... */
    form.parse(req, (err, fields, files) => {
        try {
            if (fields.tag[0] === "Login") {
                // 如果话题tag是Login说明是登录
                if (fields.name[0] === 'admin' && fields.msg[0] === 'admin') {
                    var token = jwt.sign({ name: fields.name[0] }, secret, { expiresIn: 60 });
                    console.log("进入登录模式", token)
                    res.end(JSON.stringify({
                        "success": true,
                        "token": token
                    }))
                    return
                }
            }

还需要修改app.js

// 增加全局跨域处理,因为前端将会使用json作为请求内容,那么会先发送OPTIONS请求
app.all('*', function (req, res, next) {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Headers", "Content-Type,Content-Length,Authorization,token,Accept,X-Requested-With");
  res.setHeader("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
  if (req.method == "OPTIONS") res.send(200);/*让options请求快速返回*/
  else next();
});

前端

token可以储存在sessionStorage,localStorage,我这边继续使用React.Context来储存token。

修改context.js

export const UserTokenContext = createContext(null);

修改app.js

// 引入UserTokenContext 
import { HandleClickNoContext, HandleChangeTagContext, UserTokenContext } from './util/contexts';
/* ...... */
function msgListReducer(state, action) {
  switch (action.type) {
    /* ...... */
    case 'delete':
      return { list: state.list.filter(x => x.no !== action.data) }
    /* ...... */
  }
}
/* ...... */
  // 发送表单
  const [token, setToken] = useState("");
  function handleSubmit(msg) {
    let $data = new FormData();
    for (let k in msg) {
      $data.append(k, msg[k]);
    }
    fetch("http://localhost:8080/ws/post", {
      method: "post",
      body: $data
    }).then(resp => {
      console.log(resp)
      return resp.json()
    })
      .then(res => {
        handleChangeTag(msg["tag"])
        // 如果传回的消息有token则储存token
        if (res.token) {
          setToken(res.token)
        }
      })
  }
/* ...... */
  // 初始化websocket
  const ws = useRef(null);
  useEffect(() => {
    let randomId = createRandomId()
    setRandomId(randomId);
    if (Object.is(ws.current, null)) {
      ws.current = new ReconnectingWebSocket("ws://localhost:8080/ws?id=" + randomId)
      ws.current.onmessage = function (msg) {
        let data = JSON.parse(msg.data);
        switch (data.type) {
          case 'msg':
            setMsgList({ type: 'add', data: data });
            break;
          case 'onlineList':
            setUserList(data.data.map(x => { return { id: x } }))
            break;
          case 'song':
            myRadioRef.current?.setMusic(data.song)
            break;
          case 'delete':
            setMsgList({ type: 'delete', data: data.no });
            break;

          default:
            break;
        }
      }

/* ...... */
          <UserTokenContext.Provider value={[token, setToken]}>
            <MsgContainer className="grid-item1" msgList={msgListWithTag} ></MsgContainer>
            <UserListContainer userList={userList}></UserListContainer>
            <MyRadio ref={myRadioRef}></MyRadio>
            <SubmitContainer ref={submitRef} onSubmit={handleSubmit} id={randomId}></SubmitContainer>
            <TagListContainer tagList={tagList}></TagListContainer>
          </UserTokenContext.Provider>

修改MsgBox.js

/* ...... */
import { HandleClickNoContext, UserTokenContext } from "../../util/contexts";
/* ...... */
    // 删贴与禁言
    const [token,] = useContext(UserTokenContext)
    function delMsg(no) {
        fetch("http://localhost:8080/ws/deleteMsg", {
            method: 'post',
            headers: {
                "Content-Type": "application/json",
                token: token,
            },
            body: JSON.stringify({ no: no })
        }).then(resp => {
            console.log(resp)
            return resp.json()
        }).then(res => {
            console.log(res)
        }).catch(e => {
            console.log(e)
        })
    }
/* ...... */
            <div className="msg-box_head">
                <span className="msg-box_name">{props.name || "无名氏"}</span>
                <span className="msg-box_time">{props.time}</span>
                <span className="msg-box_id">ID:{props.id}</span>
                <span className="msg-box_tag">#{props.tag || '综合'}</span>
                <span className="msg-box_no" onClick={() => handleClickNo(props.no)}
                >No.{props.no}</span>
                {token && (<span className="msg-box_no" onClick={() => delMsg(props.no)}>[删除]</span>)}
            </div>

后记

这系列文章是希望读者(包括我)能快速了解熟悉React 官方Hook的基本用法以及功能,希望读者看完这系列文章能有所收获。

如果本文中有错误的地方希望读者在评论区直接指出。

标签:console,log,no,res,......,React,Hook,token,删帖
来源: https://blog.csdn.net/sinat_39013092/article/details/121722902

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有