从头搭建一个在线聊天室(一)

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

转载声明:转载请注明出处,本技术博客是本人原创文章

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 从头搭建一个在线聊天室(一)

微信公众号:**萝卜大杂烩** 关注可了解更多的原创内容。问题或建议,请公众号留言或加本人微信; **如果你觉得文章对你有帮助,欢迎加微信交流**

今天从头开始做一个在线聊天网站,网上各种各样的聊天工具已经很多了,为啥还要做这么一个聊天工具呢,无他,兴趣耳!

今天先完成第一部分,搭建起聊天网站的整体框架。

整体技术栈

  • flask 框架
  • flask_login 的使用
  • jquery 简单应用
  • flask_login 的使用

    搭建权限框架

    还是使用 Flask 来搭建后台应用,使用 flask-login 扩展来处理用户登陆鉴权逻辑。
    首先定义登陆表单

    
    class LoginForm(FlaskForm):
        username = StringField('Username', validators=[DataRequired(), ])
        password = PasswordField('Password', validators=[DataRequired()])
        remember_me = BooleanField('Keep me logged in')
        submit = SubmitField('Log in')
    

    一个简单的登陆表单,不做过多解释

    接下来定义数据库结构

    
    class User(UserMixin, db.Model):
        __tablename__ = 'users'
        id = db.Column(db.Integer, primary_key=True)
        username = db.Column(db.String(64), unique=True, index=True)
        password = db.Column(db.String(64))
    

    当前,我们只需要一个 user 用户表,只包含三个字段的简单表。用户密码也只是简单的保存了明文,后面再处理用户密码的 hash 问题。

    下面就可以定义用户登陆表单

    
    from flask_login import LoginManager
    
    
    login_manager = LoginManager()
    login_manager.session_protection = 'strong'
    login_manager.login_view = 'login'
    app = Flask(__name__)
    login_manager.init_app(app)
    app.config['SECRET_KEY'] = 'hardtohard'
    
    
    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(int(user_id))
    
    
    @app.route('/login', methods=['GET', 'POST'])
    def login():
        form = LoginForm()
        if form.validate_on_submit():
            user = User.query.filter_by(username=form.username.data).first()
            if user:
                login_user(user)
                return redirect(url_for('chat'))
        return render_template('login.html', form=form)
    

    这里定义了,只检查用户名是否存在,如果存在,就执行 login_user() 函数,登陆。用户密码的使用,也留到后面再做处理。

    其中 load_user,是回调函数,将获取到的 user 对象存储到浏览器的 session 中,然后在调用 login_user 函数时,就会调用 load_user 来把真正需要登陆的用户设置到 session 中。当登陆成功后,就会跳转到 chat 函数所对应的页面。

    chat 函数比较简单,只是展示一个网页

    
    @app.route('/chat', methods=['GET', 'POST'])
    @login_required
    def chat():
        return render_template('chat.html')
    

    使用 login_required 装饰器,保证该函数只允许登陆的用户访问。

    增加些初始化函数

    
    @app.route('/adddb', methods=['GET', 'POST'])
    def addbd():
        db.create_all()
        return "OK"
    
    
    @app.route('/deldb', methods=['GET', 'POST'])
    def delbd():
        db.drop_all()
        return "OK"
    
    
    @app.route('/adduser/user', methods=['GET', 'POST'])
    def adduser(user):
        user = User(username=user, password='admin')
        db.session.add(user)
        db.session.commit()
        return "OK"
    

    增加了初始化数据库和新增用户的函数。

    构建前端页面

    首先处理登陆页面,在 login.html 中添加

    
     extends "bootstrap/base.html" %}
     import "bootstrap/wtf.html" as wtf %}
    
     block title %}Flasky endblock %}
    
     block navbar %}
    div class="navbar navbar-inverse" role="navigation"
        div class="container"
            div class="navbar-header"
                button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse"
                    span class="sr-only"Toggle navigation/span
                    span class="icon-bar"/span
                    span class="icon-bar"/span
                    span class="icon-bar"/span
                /button
                a class="navbar-brand" href="/"Flasky/a
            /div
            div class="navbar-collapse collapse"
                ul class="nav navbar-nav"
                    lia href="/"Home/a/li
                /ul
                ul class="nav navbar-nav navbar-right"
                     if current_user.is_authenticated %}
                    lia href=" url_for('logout') }}"Logout/a/li
                     else %}
                    lia href=" url_for('login') }}"Login/a/li
                     endif %}
                /ul
            /div
        /div
    /div  endblock %}
    
     block content %}
    div class="container"
        div class="page-header"
            h1Hello, Welcome!/h1
        /div
         wtf.quick_form(form) }}
    /div
     endblock %}
    

    使用扩展库 flask_bootstrap 来快速构建页面。

    下面重点来看看 chat 页面,主要使用了 Ajax 来处理文字交互。
    首先来看看主体页面,在 chat.html 中填入代码

    
     extends 'bootstrap/base.html' %}
     import "bootstrap/wtf.html" as wtf %}
     block title %}Kung Fu Realmendblock %}
     block head %}
    head
    meta charset="utf-8"
    titleHi Hi 聊天室/title
    link rel="shortcut icon" href=" url_for('static',filename='chat/images/hihi.jpg')}}"
    link rel="icon" href=" url_for('static',filename='chat/images/hihi.jpg')}}" type="image/x-icon"
    link type="text/css" rel="stylesheet" href="/static/chat/css/style.css"
    script type="text/javascript" src=" url_for('static', filename='chat/js/jquery.min.js') }}"/script
    /head
     endblock %}
     block content %}
    body
    div class="chatbox"
      div class="chat_top fn-clear"
          div class="uinfo fn-clear"  style="float: left;"div class="uface"h1 style="color: #7777"ROOM: 聊天室123哈哈哈/h1/div/div
        div class="uinfo fn-clear"
         if current_user.is_authenticated %}
          div class="uface"img src=" url_for('static', filename='chat/images/hi.jpg') }}" width="40" height="40"  alt=""//div
         else %}
          div class="uface"img src=" url_for('static', filename='chat/images/hi.jpg')}}" width="40" height="40"  alt=""//div
         endif %}
          div class="uname"
            小HIi class="fontico down"/i
            ul class="managerbox"
                 if current_user.is_authenticated %}
              lia href=" url_for('login') }}"i class="fontico lock"/i退出登陆/a/li
                 else %}
              lia href=" url_for('logout') }}"i class="fontico logout"/i登录/a/li
                 endif %}
            /ul
          /div
        /div
      /div
      div class="chat_message fn-clear"
        div class="chat_left"
          div class="message_box" id="message_box"
            div class="msg_item fn-clear"
              div class="uface"img src=" url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40"  alt=""//div
              div class="item_right"
                div class="msg own"img src=" url_for('static', filename='chat/images/hihi.jpg')}}" width="400" height="400"  alt=""//div
                div class="name_time"小黄鸭 /div
              /div
            /div
               if current_user.is_authenticated %}
            div class="msg_item fn-clear"
              div class="uface"img src=" url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40"  alt=""//div
              div class="item_right"
                div class="msg"Welcome to Hihi Chat Room. 欢迎来到 Hihi 聊天室。 /div
                div class="name_time"小黄鸭 /div
              /div
            /div
               else %}
              div class="msg_item fn-clear"
              div class="uface"img src=" url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40"  alt=""//div
              div class="item_right"
                div class="msg"您还没有登陆,先和小黄鸭聊聊吧。 /div
                div class="name_time"小黄鸭 /div
              /div
            /div
               endif %}
          /div
          div class="write_box"
           if current_user.is_authenticated %}
            textarea id="message" name="message" class="write_area" placeholder="说点啥吧..."/textarea
           else %}
          textarea id="message_not" name="message" class="write_area" placeholder="说点啥吧..."/textarea
           endif %}
            input type="hidden" name="fromname" id="fromname" value="你" /
            input type="hidden" name="to_uid" id="to_uid" value="0"
            div class="facebox fn-clear"
              div class="expression"/div
              div class="chat_type" id="chat_type"群聊/div
                 if current_user.is_authenticated %}
              button name="login" class="sub_but" id="sub_but_login"提 交/button
                 else %}
              button name="logout" class="sub_but" id="sub_but"提 交/button
                 endif %}
            /div
          /div
        /div
      /div
    /div
    

    整体效果如下,是不是挺少女系的。

    从头搭建一个在线聊天室(一)

    当用户在点击“提交”按钮后,调用 JS 函数

    
        /*用户登陆的用户点击提交按钮发送消息按钮*/
        $('#sub_but_login').click(function(event){
            sendMessageLogin(event, fromname, to_uid, to_uname);
        });
    

    为了后面便于扩展,将未登录的用户特别区分开来,后面也许同样允许未登陆用户访问该页面,但是只能同机器人小黄鸭聊天

    
        /*用户未登陆的用户点击提交按钮发送消息按钮*/
        $('#sub_but').click(function(event){
            sendMessage(event, fromname, to_uid, to_uname);
        });
    

    再来看函数 sendMessageLogin

    
    function sendMessageLogin(event, from_name, to_uid, to_uname){
        var msg = $("#message").val();
        var myDate = new Date();
        var myTime = myDate.toLocaleTimeString();
        var itTime = myDate.toLocaleString();
        //var iTime = myDate.toDateString();
        var htmlData =   'div class="msg_item fn-clear"'
                       + '   div class="uface" if current_user.is_authenticated %}img src=" url_for('static', filename='chat/images/hi.jpg') }}" width="40" height="40"  alt=""/ endif %}/div'
                       + '   div class="item_right"'
                       + '     div class="msg own"' + msg + '/div'
                       + '     div class="name_time"' + from_name + ' · ' + itTime +'/div'
                       + '   /div'
                       + '/div';
        $("#message_box").append(htmlData);
        $('#message_box').scrollTop($("#message_box")[0].scrollHeight + 20);
        $("#message").val('');
        setTimeout(function(){sendToServer(from_name, msg)}, 1000); //延时调用
    }
    

    接收几个参数,然后将当前会话消息追加到 HTML 页面中,并且调用真正的后台 API 函数 sendToServer

    
    function sendToServer(name, msg){
        var xmlhttp = new XMLHttpRequest();
        var myDate = new Date();
        //var myTime = myDate.toLocaleTimeString();
        var myTime = myDate.toLocaleString();
        xmlhttp.onreadystatechange=function() {
            if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
                myObj = xmlhttp.responseText;
                var htmlData2 =   'div class="msg_item fn-clear"'
                       + '   div class="uface"img src=" url_for('static', filename='chat/images/duck.jpg')}}" width="40" height="40"  alt=""//div'
                       + '   div class="item_right"'
                       + '     div class="msg"' + myObj + '/div'
                       + '     div class="name_time"' + '小黄鸭' + ' · ' + myTime +'/div'
                       + '   /div'
                       + '/div';
                $("#message_box").append(htmlData2);
                $('#message_box').scrollTop($("#message_box")[0].scrollHeight + 20);
            }
        }
        xmlhttp.open("GET", "/api/sendchat/" + msg, true);
        xmlhttp.send();
    
    };
    

    sendToServer 函数调用后台 API,并把接收到的消息回写到 HTML 页面中。

    而目前的后台 API 也比较简单,直接返回用户输入的消息

    
    @app.route('/api/sendchat/info', methods=['GET', 'POST'])
    @login_required
    def send_chat(info):
        return info
    

    这样,一个整体的聊天室架子就搭建好了,后面我们再接入 redis 和自己训练的聊天机器人,来实现真正的在线聊天室。

    猜泥稀饭:

    从头系列:

    原文始发于微信公众号(萝卜大杂烩):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    转载声明:转载请注明出处,本技术博客是本人原创文章

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 从头搭建一个在线聊天室(一)


     上一篇
    Vue + Flask 小知识(六) Vue + Flask 小知识(六)
    微信公众号:**萝卜大杂烩** 关注可了解更多的原创内容。问题或建议,请公众号留言或加本人微信; **如果你觉得文章对你有帮助,欢迎加微信交流** 今天继续分享 Vue 系列,几种前端验证 token 的方法 由于我们会有很多请求,
    2021-04-06
    下一篇 
    从头搭建一个在线聊天室(二) 从头搭建一个在线聊天室(二)
    微信公众号:**萝卜大杂烩** 关注可了解更多的原创内容。问题或建议,请公众号留言或加本人微信; **如果你觉得文章对你有帮助,欢迎加微信交流** 今天是从头开始做一个在线聊天网站系类的第二部分,完善功能,实现对话。 第一部分可以看
    2021-04-06