文章目录
  1. 1. 工程化实践
    1. 1.1. js模块化加载
    2. 1.2. gulp在工程化中的实践
    3. 1.3. FIS在工程化中的实践
  2. 2. 系统设计
    1. 2.1. 用户权限控制
    2. 2.2. 后台管理系统
    3. 2.3. 数据统计系统
    4. 2.4. 系统扩展性设计
    5. 2.5. Restful API
  3. 3. 框架的使用
    1. 3.1. ng在后台系统中的实践
    2. 3.2. ui-router的使用
  4. 4. 知识
    1. 4.1. 一个用户密码泄露的bug
    2. 4.2. session和cookie
  5. 5. 项目进度
  6. 6. 参考项目及致谢

涉及这一部分的项目正在进行中,内容正在逐渐总结。等踩坑、总结之后会再次进行文章拆分。

如果您有兴趣,欢迎来看这个项目的开发日志,这里面涵盖了这个项目开发所遇到的所有问题,并且详细记录着我对整个项目的思考过程。

工程化实践

js模块化加载

在现在的项目中,比较重要的部分,如路由器等,我们已经做了良好的模块化封装。但是对于公共工具库中的函数,我们采用了单例模式进行了封装,虽然这样的封装短平快,但是这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变正则表达式的值。

在下一次技术改版中,待公共工具库功能基本完善之后,对其进行基于CommonJS规范的的模块化。

gulp在工程化中的实践

[TODO]

FIS在工程化中的实践

[TODO]

系统设计

用户权限控制

用户权限控制方案采用session存储来进行验证,每个需要控制权限在后端的checkLogin()工具函数中通过传入不同的回调函数来处理不同的用户,并由next移交控制权

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//针对登录用户才能进入的页面
app.get('/quiz', function (req, res, next) {//问卷部分只能是登录用户才能进入
service.checkLogin(req, next, function () {
req.flash('error', '未登录!');
res.redirect('/');//重定向到常见问题页
});
});
//下级路由
//针对未登录用户才能进入的页面
app.get('/reg', function (req, res, next) {//注册部分只能是未登录用户才能进入
service.checkLogin(req, function () {
req.flash('error', '您已登录!');
res.redirect('back');
}, next);
});
//下级路由
//后端公共库中,验证用户权限的工具函数
function checkLogin(request, hasLoginCallback, notLoginCallback) {
typeof(hasLoginCallback) != 'function'?console.log('typeError') : null;
typeof(notLoginCallback) != 'function'?console.log('typeError') : null;
if (!request.session.user) {
notLoginCallback();
} else if (request.session.user) {
hasLoginCallback();
} else {
return 'checkLoginError';
}
}

后台管理系统

后台管理系统是基于angularJS开发的

草案如图:

VM设计

这里面涉及一个问题:

假设用户A回答了ID为123的问卷,那么系统会在A的用户信息中存储A的答案,并用ID123标示出它当时所回答的问题,这样当查找用户信息时,循着ID就能获取特定用户下的问卷和答案,用户信息中并不存储问卷本身。

但是,当管理员在之后修改了ID为123的问卷呢?比如说将题目调整了顺序。那么用户信息中的答案便和题目对应不上了。如何解决这类问题?总不能遍历数据库修改用户答案吧?开销太大了。

待解决

数据统计系统

[TODO]

系统扩展性设计

[TODO]

Restful API

[TODO]

框架的使用

2016.03.12

继续学习angularJS,在以前的基础上,开始看MEAN Web Devleopment一书,比ng-book更面向业务,ng-book更适合使用ng一段时间之后进行深入阅读。

根据ng的特性,开始抽象用户页的view model,在抽象的过程中,根据实际情况和未来功能的可扩展情况,优化之前写好的user/question/quiz模型

ng的学习曲线果然要陡峭一些,不是难在MVC,这套思想之前在iOS开发中体会较深。难在它对html标签的扩展上,很多功能脱离了原生js和jq,事件和标签又混合在了一起,如何用数据去驱动视图,这里的思路和用法需要重新构建。总结:think in ng不难,难在code in ng。

ng路由处理2-3个(答题记录/个人信息/问卷管理页&志愿者工作页)页面之前的跳转

每个页面对应一组view model和view

ng中只需要关心数据的改变,不用关心与数据关联视图的改变,因为他们的改变会由ng的双向绑定自动处理

先设计个人信息页(包括修改保存功能)

[todo]判断用户组时,用户组用数组存储,还是字符串?

用户页不要SEO!!#!

在编辑问卷页添加的问题也添加到问题库中

2016.03.13

今天才有点搞明白了编写ng应用时正确的思考方式,比如做选项卡标签切换样式时,我们要做的是把所有标签的样式写成一个表达式ng-class='$index===current? 'active' : ''',然后在控制器中有一个'current'变量,每次点击选项卡事件发生时,将当前的$index赋给current,剩下的事情就都交给ng了,当点击第一个选项卡时,current=0,然后$index为0的选项卡样式自动变成了active,这个自动的部分是有ng自动完成了,我不再需要去关心任何DOM操作。

调试路由时遇到了 couldnot resolve state错误,经过查找这里解决

利用ng实现了管理页面中的选项卡切换,但还有问题,如何实现选项卡和地址和下面内容div之间的绑定?现在的情况是,点击选项卡时地址和下面的内容可以切换,反之当改变网址时,选项卡不跟着变;一旦之前看过某个内容,再刷新时,选项卡会归零,但内容还是之前看过的内容

2016.03.16

上面的问题,使用另一个小项目实验了多次都没有成功,虽然没有实现,但是看到了一篇文章,帮我缕清了ng中的作用域之间的关系,解答了为什么ng建议尽量不要把数据以基本数据类型来存储

最后,上面问题的解决方案是使用ui-router中的ui-sref-sctive指令来更新样式,解决问题,参考了这里

还使用了嵌套路由来实现了问卷编辑页面的进入

ng在后台系统中的实践

[TODO]

ui-router的使用

[TODO]

知识

一个用户密码泄露的bug

在验证用户登录时,后端的公共库中使用了一个函数,传入用户名和密码,传出一个布尔和数据库中获取的用户信息。

这个函数这样设计是为了在数据库一次读取中能够返回足够多的数据供后台使用,避免重复读取数据库造成不必要的开销。但是在上面的使用条件下,我将返回值直接传递给了前端,造成了包括密码等用户信息的泄露。那么有没有必要为了前端在单独写一套保密的处理函数呢?肯定是没有必要的。

之后我对这个工具函数做了修改,通过传入选项来确定到底是只返回布尔值还是布尔值加用户数据,这样的话避免了用户信息泄露,也避免了为了过滤敏感信息单写一套处理函数。代价是当后台调用的同时也需要用户信息的时候,会多一次数据库读取:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function verifyUnique(collectionName, forCheck, callback, returnInstance) {
try {
// console.log(forCheck, callback, returnInstance);
collectionName.get(forCheck, function (err, instance) {
if (err) {
callback({error: err});
} else {
if (instance.length!=0) {
returnInstance?(callback({error:null, code:'02', instance:instance})):(callback({error:null, code:'02', instance:null}));
} else {
callback({error:null, code:'01', instance: null});
}
}
});
} catch (error) {
console.log(error);
}
}

session和cookie

知识来自这里:session机制详解以及session的相关应用

web应用开发里就出现了保持http链接状态的技术:一个是cookie技术,另一种是session技术。

cookie技术是客户端的解决方案(当然随着html5的出现,比cookie更为强劲和安全的技术出现了,但是鉴于html5的普及度不够,就不做本文讨论的内容了),Cookie就是由服务器发给客户端的特殊信息,而这些信息以文本文件的方式存放在客户端,然后客户端每次向服务器发送请求的时候都会带上这些特殊的信息。让我们说得更具体一些:当用户使用浏览器访问一个支持Cookie的网站的时候,用户会提供包括用户名在内的个人信息并且提交至服务器;接着,服务器在向客户端回传相应的超文本的同时也会发回这些个人信息,当然这些信息并不是存放在HTTP响应体(Response Body)中的,而是存放于HTTP响应头(Response Header);当客户端浏览器接收到来自服务器的响应之后,浏览器会将这些信息存放在一个统一的位置,对于Windows操作系统而言,我们可以从: [系统盘]:\Documents and Settings[用户名]\Cookies目录中找到存储的Cookie;自此,客户端再向服务器发送请求的时候,都会把相应的Cookie再次发回至服务器。而这次,Cookie信息则存放在HTTP请求头(Request Header)了。有了Cookie这样的技术实现,服务器在接收到来自客户端浏览器的请求之后,就能够通过分析存放于请求头的Cookie得到客户端特有的信息,从而动态生成与该客户端相对应的内容。通常,我们可以从很多网站的登录界面中看到“请记住我”这样的选项,如果你勾选了它之后再登录,那么在下一次访问该网站的时候就不需要进行重复而繁琐的登录动作了,而这个功能就是通过Cookie实现的。

session技术则是服务端的解决方案,它是通过服务器来保持状态的。由于Session这个词汇包含的语义很多,因此需要在这里明确一下 Session的含义。首先,我们通常都会把Session翻译成会话,因此我们可以把客户端浏览器与服务器之间一系列交互的动作称为一个 Session。从这个语义出发,我们会提到Session持续的时间,会提到在Session过程中进行了什么操作等等;其次,Session指的是服务器端为客户端所开辟的存储空间,在其中保存的信息就是用于保持状态。从这个语义出发,我们则会提到往Session中存放什么内容,如何根据键值从 Session中获取匹配的内容等。要使用Session,第一步当然是创建Session了。那么Session在何时创建呢?当然还是在服务器端程序运行的过程中创建的,不同语言实现的应用程序有不同创建Session的方法,而在Java中是通过调用HttpServletRequest的getSession方法(使用true作为参数)创建的。在创建了Session的同时,服务器会为该Session生成唯一的Session id,而这个Session id在随后的请求中会被用来重新获得已经创建的Session;在Session被创建之后,就可以调用Session相关的方法往Session中增加内容了,而这些内容只会保存在服务器中,发到客户端的只有Session id;当客户端再次发送请求的时候,会将这个Session id带上,服务器接受到请求之后就会依据Session id找到相应的Session,从而再次使用之。正式这样一个过程,用户的状态也就得以保持了。

由此我们可以得出,session是解决http协议无状态问题的服务端解决方案,它能让客户端和服务端一系列交互动作变成一个完整的事务,能使网站变成一个真正意义上的软件。

cookie与session的关系

cookie和session的方案虽然分别属于客户端和服务端,但是服务端的session的实现对客户端的cookie有依赖关系的,上面我讲到服务端执行session机制时候会生成session的id值,这个id值会发送给客户端,客户端每次请求都会把这个id值放到http请求的头部发送给服务端,而这个id值在客户端会保存下来,保存的容器就是cookie,因此当我们完全禁掉浏览器的cookie的时候,服务端的session也会不能正常使用(注意:有些资料说ASP解决这个问题,当浏览器的cookie被禁掉,服务端的session任然可以正常使用,ASP我没试验过,但是对于网络上很多用php和jsp编写的网站,我发现禁掉cookie,网站的session都无法正常的访问)

项目进度

完成到后台ng应用,已经可以进行路由嵌套切换,正在进行问卷编辑功能的实现

参考项目及致谢

  • 参考资料1

    参考资料1

    这个项目值得借鉴的是它的SurveyRazor.js问卷渲染引擎,里面似乎实现了包括问卷类在内的所有功能,引入网页中时只需要下面的代码:

    1
    2
    3
    4
    5
    6
    7
    8
    //需要事先引入数据文件
    //<script src="JsonData.js" type="text/javascript"></script>
    SurveyRazor.dataStore.load(data);
    SurveyRazor.surveyRazor.options({
    description: desc, //描述
    haveBgImg: false, //启用背景图片
    surveyTitle: "客戶滿意度調查表"
    }).create().show();

    可是作者并没有给出json源数据及任何参考文档,我们需要手工分析这个引擎的结构,及使用方法

    最后这个项目中参考了它对问卷分类及对问题中必要属性的设置,我根据实际需要用模板引擎实现了一套渲染引擎,避免了这个项目中繁杂的字符串拼接,而且分离了问卷行为和视图,不再像这个项目中一样都耦合在一起,灵活性很差

  • 参考资料2

    参考资料2

    这个项目值得借鉴的是它的后台系统设计,完成了问卷问题的增删改及数据统计工作。

    缺点是它的问题类型只有有限的几种,没有前者那么丰富;作者采用了react和VUE来开发的,虽然也能大概看清业务逻辑,但时间成本略高

  • 参考资料3

    参考资料3

    它的Github

    教程

    这个项目值得参考的是它的后台系统设计,完成了问卷问题的增删改及预览工作。

    缺点是它和我们的业务并不完全一样,主要展示了angularJS的双向绑定和构建单页应用

    而且没有后台统计和权限管理功能,全部由angularJS来处理,连jade模板都不用了,都是ng模板直接拼接

    然而难能可贵的是作者的博文中有学习angularJS的笔记,可以共学习angularJS时参考

    最后主要从其博客中学习了angularJS

  • 参考资料4

    参考资料4 更多

    这个是国外小哥写的4个问卷项目,还没细看

  • 参考资料5

    参考资料5-n-blog

    主要借鉴了这个项目中使用mongodb原生驱动的操作,用户类行为设计,session/flash的操作

    最后主要借鉴了它对session/flash的操作

  • 参考资料6

    参考资料6

    MONGODB 增删改查官方手册

    MONGOOSE 官方api手册

    实现id自增的官方方法

    最后主要选择了mongoose来操作数据库,同时对资料3的代码进行了重构

    有些方法调用语法没变,方法内部没做什么特殊操作,mongoose也提供了原生方法,实在没有必要再封装一次。项目三的作者从java出身,封装的习惯确实蛮好,但这里我认为实在没有必要封装,藉由js模块已经可以处理的很好了,没有必要对行为类再次抽象

    Each document can be saved to the database by calling its save method

文章目录
  1. 1. 工程化实践
    1. 1.1. js模块化加载
    2. 1.2. gulp在工程化中的实践
    3. 1.3. FIS在工程化中的实践
  2. 2. 系统设计
    1. 2.1. 用户权限控制
    2. 2.2. 后台管理系统
    3. 2.3. 数据统计系统
    4. 2.4. 系统扩展性设计
    5. 2.5. Restful API
  3. 3. 框架的使用
    1. 3.1. ng在后台系统中的实践
    2. 3.2. ui-router的使用
  4. 4. 知识
    1. 4.1. 一个用户密码泄露的bug
    2. 4.2. session和cookie
  5. 5. 项目进度
  6. 6. 参考项目及致谢