从前端到后台:一个聊天项目带你撸全栈

差不多花了整整两个星期,终于把这个聊天APP的后台架构搭建出来了。虽然花的时间比较多,但这也是我第一次写后台,其实也并没有想象中的那么难,但也还是很折腾,尤其是在数据库这一块,几乎全部都是英文文档(看得都只想**)。

项目概述

该聊天App高仿iOS端的微信,当然没这么复杂,目前已实现功能有:

  • 用户注册、登录、注销功能;
  • 自动缓存已登录用户,关闭浏览器窗口失效;
  • 聊天室:所以在线用户之间聊天;
  • 与在线用户之间聊天;
  • 获取所有在线用户;
  • 获取好友列表;
  • 添加好友:后台接口已完成,前端目前尚未实现。

现在几乎每天都在更新,争取把它做得更像一个正规的聊天应用。不过由于该应用是基于Web页面的,用户体验和数据持久化等诸多方面肯定没法跟客户端应用相比。

前端Web界面

前端界面在一个多月前就已经差不多写出来了,苦于一直没有后台接口(API)的支持,所以仅仅只是一个界面展示,并无实际聊天的功能。

对前端我就不做深入的介绍了,主要是基于Vue来实现的。而且对于一个前端开发者来说,后台实现可能更具有挑战性。

后台实现

为了实现真实的聊天功能,我决定自己来搭建后台,这也是我第一次写后台。整个后台应用基于Node.js平台,采用express模块来搭建HTTP服务器,聊天功能采用WebSocket实现,数据库使用的是MongoDB

主要使用的技术栈包括:Node.jsexpressexpress-sessionexpress-wsmongodbmongoose

后台逻辑分析

初次写后台,最难的可能就是架构了,因为你要对整个应用的需求、实现的功能、数据的模型等有一个清晰的思路逻辑。我可能也就是在这方面花的时间是最多的,总是不知道该如何下手。很多次都是写着写着就写不下去了,因为逻辑行不通了。

遇到的问题和难点

  • 如何判断用户是否是登录状态?如何记住用户的登录状态?
  • 如何断定当前登录的用户是否成功连接了WebSocket服务器?
  • 当一个来自客户端的websocket请求时,如何判断该用户是否已登录?需要一个身份识别功能,否则谁都可以任意接入websockt服务器了。
  • HTTP服务器与WebSocket服务器之间如何并存?又如何交互?因为只有聊天功能和消息推送功能使用ws,其他所有的请求都是与http服务器通信。
  • ws服务器如何判断消息的转发目标?如果目标用户不在线又如何处理?
  • 如何搭建数据库?对于初次接触的人来说这也是个难题。
  • 如何连接和操作数据库?起码要基本的增删改查。
  • 密码加密问题,这同样是一个很大的难题。

其实问题还有很多很多,这可能对于后台开发人员来说都显得小儿科,但这些真是我开发过程中遇到的问题,当然还不止如此。到目前为止,有的问题已经解决了,有的问题仍未解决,或没有找到更好的解决方案。

其实,学习也就是一个发现问题,然后解决问题的过程。当你把一个一个的问题都解决之后,你也就在不知不觉中慢慢成长起来了。贵在坚持,也难在坚持。

模块介绍

对于上面的问题,我也是自己网上找资料,目前主要引用到了这些模块框架:

  • express:基本上是整个后台应用的支撑,HTTP和ws都是建立在此基础之上。一个Node.js上很强大的东西,可以让你快速创建一个Web应用。
  • express-session:这个是express的插件,主要用来解决上面说到的判断用户是否登录的问题。
  • express-ws:这也是一个express的插件,用来构件一个ws服务器。之前采用的是ws框架,但与express交互性太差,不好在wshttp之间通信。
  • body-parser:一个express框架,主要用来解析POST请求发过来的数据。
  • mongoose:一个用来操作mongodb数据库的框架。还有一个叫做mongolass的框架,比这个量级要轻。

主目录结构

由于是第一次写后台,后台结构分的并不是很清晰。

  • index.js:入口文件,创建一个http服务器和一个ws服务器,并连接到数据库。
  • model:该目录主要写一些与数据库交互的代码。
  • routes:这个目录主要处理路由,大部分的操作都是在该目录下进行的。

主要的架构就是这样,基本操作都在routes目录下,因为后台也就是为前端写接口。在routes目录下又分了不同的子路由,比如:frienduserwsmessage等,分别处理不同的请求。

看起来很简单,但做起来真的不容易,最可怕的是代码量大了,你会陷入一个大量重复代码和无限回调的噩梦,我想大部分人都经历过js的回调噩梦。目前也只是有了个初步的逻辑架构,后面可能会根据需求的不同而变更。代码也需要优化,有的自己一遍一遍写起来就恶心。

两个容易误会的概念

本篇文章主要作个整体的介绍,因为该Web应用目前仍在开发中,很多功能还不确定,等后面整个逻辑清晰了再作总结。下面说两个很经典的问题,也是前端很容易误会的问题,至少我是误会了很久。

跨域

我的前端页面是托管在GitHub上的,通过开启静态页面的功能,可使用域名来访问http://mohng.com/wchat-vue。而我的后台是搭建在自己的服务器中的,所以自然就面临了一个问题:跨域访问。

在这之前,对跨域访问是一知半解,不知道到底该如何解决这个问题。这里要提出,跨域访问不是前端的问题,其实大部分都是后台的问题。对于跨域,网上有两种解决方案:JSONP和Ajax。对于JSONP没什么研究,不作介绍,好像也并不是很实用,这里主要介绍Ajax跨域的问题。

下面是我后台解决跨域问题的方案:

app.use((req, res, next) => {
    res.set({
        // 跨域cookie 不能为通配符 *
        'Access-Control-Allow-Origin': 'http://localhost:8808',
        'Access-Control-Allow-Methods': 'GET,POST',
        // 跨域cookie必须为true
        'Access-Control-Allow-Credentials': true
    });
    next();
});

简单的说一下,跨域其实浏览器是可以正常的收到来自于服务的响应,只是无法正确的解析。通过在服务器端对响应头写入'Access-Control-Allow-Origin': '*''Access-Control-Allow-Methods': 'GET,POST',浏览器才能正确的解析服务器的响应。记住是在服务器端对响应头的操作,我之前一直误会是在前端的请求头中写入,现在想想有点傻逼了。

对于'Access-Control-Allow-Credentials': true,是用来处理跨域中cookie的问题。因为默认情况下,cookie是不允许在跨域访问中传输的。要解决这个问题,Access-Control-Allow-Origin的值就不能为通配符*,并且前端通过Ajax发起请求时也要做处理。

$.ajax(url, {
    method: 'GET',
    xhrFields: {
        withCredentials: true
    },
    ...
})

Cookie

之前对Cookie的认识一直就是一种类似于缓存的东西,但具体是做什么,怎么用,并不清楚。这是要指出两点:

  • Cookie基本上都是由后台来管理的,前端不需要任何操作
  • Cookie信息会在每次发起的请求中自动携带

那么,这下就清晰多了。如果你仅仅只是搞前端,基本上是用不到Cookie的。虽然也可以通过js代码读取到cookie数据,但大部分服务器都是禁用掉此操作,也就是让你在前端无法通过js代码读取到cookie的内容,读取到的是空字符串。

因为cookie是每次发起请求都会自动携带的,所以服务器就可以通过cookie来识别用户的身份、是否处于登录状态等,就像你进入某个网站有时候会自动识别你的身份并登录。而cookie也是可以设置过期时间的,所以服务器端就可以控制你的身份多久失效,失效之后你就要重新登录了。

你可以自己尝试在浏览器的控制台通过document.cookie来获取一下网站的cookie信息。也可以尝试清除浏览器的cookie,然后再刷新你登录的网站,看是否需要重新登录。

我这个项目中用到的express-session就是通过cookie来识别用户身份的。使用express-session的好处就是你不需要自己要操作cookie,使用起来简单。

后记

我一般写文章都是针对自己实际遇到的问题来的,我目前也是在不断的学习中,过几天就会写一篇文章作个总结。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • HTTP cookie(也称为web cookie,网络cookie,浏览器cookie或者简称cookie)是网...
    留七七阅读 17,809评论 2 71
  • 作者:晚晴幽草轩www.jeffjade.com/2016/10/31/115-summary-of-cookie...
    饥人谷_Dylan阅读 1,222评论 0 51
  • 背景在HTTP协议的定义中,采用了一种机制来记录客户端和服务器端交互的信息,这种机制被称为cookie,cooki...
    时芥蓝阅读 2,353评论 1 17
  • 注:本文转载自前端大全 背景 在HTTP协议的定义中,采用了一种机制来记录客户端和服务器端交互的信息,这种机制被称...
    楠小忎阅读 668评论 0 0
  • cookie cookie的起源 早期web刚开始出现复杂的应用程序时,产生了对于能够直接在客户端上存储用户信息能...
    zenggo阅读 3,805评论 1 52