首页 > 技术文章 > 分页参数处理逻辑的最佳实践

BillySir 2021-02-20 00:11 原文

  本文讲的不是如何结合数据库实现分页功能,而是对分页参数pageNum和pageSize缺省时的处理逻辑。结合工作经验,谈谈不同方案的优缺点,并推导出最佳方案。

 

参数:pageNum, pageSize

pageNum 指的是拉取第几页的数据(也叫记录,下同),从1开始。
pageSize 指的是每页多少条数据。特别地,当pageSize=0时,代表拉取全部数据,此时pageNum无意义。

需求:
1. pageNum和pageSize能不传就尽量不传,方便使用。

方案A

  • pageNum缺省是1
  • pageSize缺省是0

当两个参数都不传时,自然就是返回全部记录。

优点:规则简单,好理解。
缺点:遇到大表时,开发人员一不小心调用就卡死了。卡死前端,也可能连后端都卡死。
开发人员刚接触一个新API时,正常的想法是什么参数都不传,调用一下看什么反应。

所以需求改为:
1. pageNum和pageSize能不传就尽量不传,方便使用。
2. 避免不小心返回表的全部记录。

方案B

  • pageNum缺省是1
  • pageSize缺省是20

当两个参数都不传时,就是返回第1页记录。

若要返回全部记录,则必须传pageSize=0。

优点:不传参数时,无论表有多少记录,再也不会出现卡死的情况了。
缺点:前端不知道某些接口是有分页的,没传分页参数,某些表(比如管理员表)一开始数据量小没发现问题,使用一段时间后,超过1页了,超过的就显示不出来。这个坑不太容易发现。而且不是一两个接口有这个问题,工作量不小。生产环境修复Bug,得小心谨慎。

  不传分页参数时,应该如何处理才是最佳做法(最佳实践)是个值得三思的问题。

需求再次修改为:
1. pageNum和pageSize能不传就尽量不传,方便使用。
2. 避免不小心返回表的全部记录。
3. 避免前端不知道分页机制的存在,没传分页参数,得不到第1页以外的数据。

分析:
若pageNum和pageSize都空,则不能返回全部记录,也不能返回第1页。
既然,不能返回全部,也不能返回第1页,更不能返回第n页,那就只能报错了。

什么条件下报错,报什么错?
1. 有pageNum,有pageSize,正常执行。
2. 有pageNum,有pageSize,且pageSize=0,正常执行。
3. 无pageNum,有pageSize,且pageSize=0,正常执行。
4. 有pageNum,无pageSize,pageSize取默认值,正常执行。
5. 无pageNum,有pageSize,且pageSize不是0,报错:pageNum不能空。
7. 无pageNum,无pageSize,报错:pageNum不能空。

无pageSize,则pageSize是null不是0

方案C

  • 有pageNum时,若无pageSize,则pageSize设为20。
  • 无pageNum时,若pageSize不是0,则报错,错误信息:pageNum不能空。

优点:
1. 有pageNum时,pageSize可以不传;pageSize传0时,pageNum可以不传,满足需求第1点。
2. pageNum和pageSize都不传时报错,避免了返回全部记录,满足需求第2点。
3. pageNum和pageSize都不传时报错,强迫前端pageNum和pageSize至少传一个,前端也就知道这个接口有分页功能,满足需求第3点。

  方案C就是最佳实践。我孜孜不倦地追求最佳方案,以不变应万变。

  By the way, 关于pageSize默认20,成了业界的不成文规定,好像谁也没有要打破的意思,对此,我有看法。没有谁规定一页的默认记录数非20不可。鉴于目前互联网和通信的发达,我个人推荐一页默认50条记录。当你遇到一个软件,每次拉取20条记录,每次拉取都要等待一两秒,而你想想拉取500条记录,操作起来想死的心都有了,你就会觉得每页20条,是多么不合理。


  上面的文章发表后,收到一位好朋友的反馈:如果pageSize不控制大小,那么容易造成CC攻击。他是对的!我前面考虑的都是如何防范无意之过,而忽略了防范有意攻击。而且,我觉得我可以为防范这种攻击做点什么。

pageSize限制为多少?
  限制为多少这个没有一个标准。太小会造成不便,比如某些数据有两三百行,若前端想一次装载,而后端限制pageSize比较小,则前端得分成多次请求再做拼装,太不方便了。但太大也确实不行,几千几万就越来越有攻击的味道了。思前想后,最终定在了500这个数字。500是默认值的10倍,易记。一次请求500行也不至于造成攻击。仅供参考。做法也简单,如果前端传来的pageSize大于500,就直接修改为500,返回的结果中是带有pageSize的,不至于被前端不知道实际执行的pageSize是多少。

要不要禁止pageSize=0?
  既然pageSize最大值是500了,还要不要支持pageSize=0?它的意义何在?假设不支持0,而前端想要每页的数据“尽可能多”,应该是传500。后来某一天后端把最大值调整为600了,前端得把500也改为600。你可能会说,不用,一开始前端就传800,甚至最大的数,比如2000,反正是一个足够大的数。这不,0就是代表这个足够大的数。是的,所以得支持0。

方案D:

  • 有pageNum时,若无pageSize,则pageSize设为50。
  • 无pageNum时,若pageSize不是0,则报错,错误信息:pageNum不能空。
  • 有pageSize时,若pageSize大于500,则修改为500。
  • 若pageSize=0,则修改为500。

嗯。方案D成为最佳实践。

 

推荐阅读