并不简单的基础开发工作(二):信息展示列表

本文以被各大学校学生做滥了的“学生信息管理系统”为例,讲述信息展示列表开发中的一些问题。

需求

本文只讨论一个页面:学生信息管理系统中的学生列表页面,大体上样式如下(PC浏览器)

身份证号码: 姓名: 学号:

序号 身份证号码 姓名 性别 民族 户籍省份 区县 出生日期 学院 班级 学号 联系电话
1 1234...78 一花 汉族 北京市 西城区 19XX年X月X日 计算机与软件工程学院 软件1901 201901010101 13012345678
2 1234...79 二乃 汉族 上海市 黄浦区 19XX年X月X日 计算机与软件工程学院 软件1901 201901010102 13012345679
3 1234...80 三玖 汉族 广东省 广州市 越秀区 19XX年X月X日 计算机与软件工程学院 软件1901 201901010103 13012345680
4 1234...81 四叶 汉族 浙江省 杭州市 西湖区 19XX年X月X日 计算机与软件工程学院 软件1901 201901010104 13012345681
5 1234...82 五月 汉族 江苏省 南京市 鼓楼区 19XX年X月X日 计算机与软件工程学院 软件1901 201901010105 13012345682

每页显示条,共49条 /5页

原理

列表页面的原理比较简单,不考虑分页的话,SELECT idcard, name, gender, ... FROM students WHERE 一些条件,然后将查出来的结果全部展示到页面即可。但是加上分页之后逻辑会复杂很多,因此建议事先将分页功能封装成公共函数或组件(本文不再讨论分页的实现方式)。

对于有固定取值的数据(性别、省市县、学院等),建议准备专门的字典表或配置文件,展示时从字典中取值。建议将读取字典数据也维护成公共函数或组件。

设计方面的问题

屏幕分辨率

开发人员经常使用大显示器,分辨率比较高,而用户的显示器五花八门,有大有小,甚至在当今这个满街液晶显示屏的时代仍然使用大脑袋瓜子的1024x768 CRT显示器。因此,设计页面时要考虑多种显示器宽度和高度,例如采用响应式布局,或者去除不必要的东西,或者加水平滚动条,总之要避免让窄屏用户看到像本博客上面那样“东西装不下”的页面效果。

如果你的显示器分辨率比较高,建议把一些常用的宽度和高度测量好,贴在你的显示器上,并且在调试的时候测试不同尺寸的浏览器窗口。

不清楚实际数据规模

一个班级大约有二三十人,而一个年级会有几十至几百人,一个学校可能超过上万人。如果不考虑实际数据规模,设计和实现出来的东西有可能会使用户困扰,例如没有分页(然后把全校上万人全部展示出来)、缺少导入功能(上万人信息需要一个一个地录入)和批量操作功能(上万工作单需要一个一个地提交)。

即使故意要求给用户增加障碍,例如审核工作单时必须一个一个地操作,我们其实也可以设法在不违反原则的前提下给用户提供一些便利,例如审核完成后自动跳转到下一工作单,并提醒还有多少工作单未处理,而用户不想处理的单子也可以轻松跳过。

没有搜索/搜索条件不足

数据很多的时候,应当给用户提供搜索功能,便于很快从大量数据中找到需要的内容。另外设计查询条件时要了解用户希望如何定位数据,避免漏掉常用条件或提供很多多余条件,例如去营业厅查电话费,以身份证或手机号中的前几位或后几位作为查询条件比较靠谱,而用姓名和性别作为条件就不靠谱。

对于上面的表单来说,查询条件里便缺少学院、班级和学号,并且身份证号和学号最好是模糊查询,因为除了学生本人以外很少有人能把这些号码记得一个数字都不差,而且就算拿着本人身份证或学生证,敲数字也挺麻烦的。

排序

展示数据最好排个顺序(默认排序顺序应当取决于用户经常做什么事情),而且要让用户能选择给哪一列排序,例如拿学生名单核对数据时会希望按学号排序,找人的时候会希望按姓名拼音排序,统计生源地时会希望按出生地排序……数据库里面什么顺序就展示什么顺序的话,没准会让用户头大。

个人隐私

身份证号、手机号等数据属于个人隐私,没有必要的话不要随意展示给用户。如果确实需要展示,建议考虑脱敏处理(即使本人录入的也是,谁敢说不会盗号呢),例如130****5678。

展示逻辑删除的数据

对于删除的数据,无论是物理删除还是逻辑删除,只要删除就没必要再展示给用户了(除非另外设计“回收站”功能)。例如上面表单中有一个“状态”,如果“删除”的操作只是把这个“状态”由有效变成无效,那么建议直接去掉本列,而且使“无效”的数据不再呈现。

实现方面的问题

跨站脚本攻击

举个例子,在数据库里维护一个名字叫张三<script>alert('xss');</script>的学生,若加载页面时弹出个窗口,说明页面有跨站脚本攻击的风险。

跨站脚本攻击对策有多种,建议用比较彻底的一种:将页面上的动态输出设置为默认转义。换句话讲,用<%= name %>这类模板输出内容时,让模板引擎默认自动将其中的HTML代码转义,这样在页面上展示的就是张三<script>alert('xss');</script>而非张三和一个弹框。(不过前提是模板引擎支持。如果不支持,那么建议做一个统一的转义标记,并要求输出用那个统一标记)

性能

查询要注意性能问题。例如

数据库索引

如果该建索引的地方没建索引,或者SQL写得很糟糕,根本没走索引,那么查询速度自然会变慢。

分页

以Oracle为例

SELECT * FROM (
    SELECT s.*, s.ROWNUM rn FROM (
        SELECT * FROM students where ...
    ) s WHERE rownum<=10000000+10
) WHERE rn>=10000000;

的效率要比

SELECT * FROM students WHERE ROWID IN (
    SELECT id FROM (
        SELECT s.ROWID id, s.ROWNUM n FROM (
            SELECT * FROM students WHERE ...
        ) s WHERE rownum<=10000000+10
    ) WHERE n>=10000000
);

低一些,因此建议按后者查询。

反复查数据库

出生地、民族等字典数据可能也存放在数据库中。如果每次获取取值都查一遍数据库(特别是循环里面查数据库)会非常影响性能。由于字典数据变化并不频繁,建议将此类数据做成缓存,一次性从数据库查好并缓存之后就直接从缓存里面取值。

缓存会有缓存时效以及线程安全等问题,这里不再展开讨论。

模糊查询

模糊查询,特别是双“%”(LIKE '%字符%')会影响性能,如果不能去掉模糊查询功能,那么要控制好条件,避免大幅度的表扫描。

越权访问

假如查看的链接是“/students/view.do?id=10001”,那么将id=10001改成不属于你的10002,你能否看到数据呢?如果能,说明存在漏洞,应当在查数据时附上身份验证(例如在SQL加上是创建者本人的查询条件),确保用户只能看到自己的数据。

拒绝服务

假如查询查出总共10000条数据(当然是把分页之后的数据合到一起),你能否通过修改请求的方式让每页展示10000条数据?如果能展示,而且性能开销还不小,那么别人也可以用类似的方法使系统变慢甚至瘫痪。

你的查询耗时长吗?如果耗时比较长,而且SQL层面的优化已经做得很到位,无法再进一步提高效率,那么建议增加按钮变灰(点一下查询按钮之后就无法再点击,直到查询完成)、动画反馈(Loading...)和限制操作频率(例如禁止频繁操作,或要求先输入验证码再查询)等措施,以免用户频繁提交影响系统整体性能。

SQL注入

老生常谈的问题,不再详细讨论。假如你在某个查询条件框输入' or '1'='1却能查出很多数据,那么你要赶紧补漏洞了。

未防止误操作

删除是一个“有危险性”的操作,如果删除之后重建比较麻烦,那么当用户点击“删除”按钮时,系统最好不要直接执行删除,而是弹出一个提示框,让用户确认无误之后再删,以免用户误操作。提交工作单(提交成功之后就不能再修改内容了)之类的“重大”操作也是如此。

本系列目录

  1. 登录页面
  2. 信息展示列表
  3. 信息录入表单
  4. 业务申办-审批流程