外观
请求与数据工具
Base 中的工具服务于请求处理和轻量数据整理,不注册对应 HTTP 接口。以下工具位于 com.own.component.base.util,可在应用服务或 Controller 中直接调用;统一响应和 JSON 规则见使用与配置。
读取请求数据
| 方法 | 行为与边界 |
|---|---|
| RequestUtil.header(request) | 返回 Map<String,String>,每个名称使用 getHeader 取得一个值,不保留同名 Header 的全部值 |
| RequestUtil.headerForDecode(request) | 对 Header 值执行 URI 解码 |
| RequestUtil.header(request, valueFunction) | 逐值转换;单项读取或转换异常被记录并跳过 |
| RequestUtil.cookies(request) | 返回 Cookie 名值 Map;同名 Cookie 后值覆盖前值,没有 Cookie 时为空 Map |
| RequestUtil.body(request) | 按 UTF-8 逐行读取输入流、拼接为字符串并关闭流;原换行符不会保留,IOException 时返回空字符串 |
body() 不缓存请求,也不保证后续还能由 @RequestBody 再次读取;不要在正常 MVC 反序列化之前调用它来做旁路日志采集。它没有大小上限,空串也无法区分空正文和读取失败;完整正文和重复读取的生命周期应由应用统一管理。
Header/Cookie 工具不筛选或脱敏返回值。只取业务需要的字段,避免把完整 Map 作为公共接口数据返回。
取得 IP 并匹配规则
IpUtil.getIpAddress(request) 先使用 request attribute calc-ip 中的非空值,再依次尝试以下 Header,最后回退到 request.getRemoteAddr(),并将结果写回 calc-ip:
text
X-Original-Forwarded-For → X-Forwarded-For → x-forwarded-for
→ Proxy-Client-IP → WL-Proxy-Client-IP → HTTP_CLIENT_IP
→ HTTP_X_FORWARDED_FOR → X-Real-IP选中的 Header 为逗号分隔列表时,取首个非空且不等于 unknown 的项。该方法不验证代理来源,也不验证返回项一定是合法 IP;代理 Header 必须由应用的可信入口清洗。它与系统日志默认发起人使用 remoteAddr 的策略不同,不应互相替代而不检查部署规则。
IpUtil.matches(ip, rule) 先验证被匹配的 IP 为 IPv4,再按规则选择匹配方式,解析异常通常返回 false:
| 规则形式 | 示例 |
|---|---|
| 单地址 | 192.168.1.10 |
| 通配符 | 192.168.1.* |
| CIDR | 192.168.1.0/24 |
| 地址范围 | 192.168.1.10-192.168.1.50 |
本方法不支持 IPv6。isValidIpv4(value) 是单独的格式判断,但不接受 null;matches(null, rule) 则返回 false。
当前规则解析不是完整的规则校验器:通配符模式直接转换为正则;CIDR 未校验前缀长度,/0 的移位实现不符合全地址网段语义;范围地址转换使用有符号 int 累积,跨越 IPv4 高低半区的范围可能误判。用于访问控制前,应在应用边界限制规则格式并核对上述边界,不能把方法名称当作完整 ACL 保证。
计算内容摘要
FileMd5Util.get(...) 支持 String、byte[]、File 和 MultipartFile,返回小写十六进制 MD5。字符串按 UTF-8 编码,例如:
java
import com.own.component.base.util.FileMd5Util;
String digest = FileMd5Util.get("hello");
// 5d41402abc4b2a76b9719d911017c592File 入口使用内存映射读取文件;MultipartFile 入口先通过 getBytes() 读取完整内容到内存,不是有界流式计算。文件读取的 IOException 转成“获取文件md5失败”业务异常,其他运行时异常没有在这些入口统一兜底。
checkPassword(password, md5PwdStr) 仅计算字符串 MD5 并比较,没有加盐或工作因子,不是完整的密码存储方案。摘要可用于已有内容标识协议,身份凭据应沿用应用的密码验证实现。
整理轻量结果
这些类型位于 com.own.component.base.model.entity,只提供数据容器与少量方法,不带持久化、分页、认证或接口注册。
| 模型 | 主要字段与用法 |
|---|---|
| EntityModel | id: Long、name,表示简单选项 |
| ContentModel | content;显式调用 check() 拒绝空白内容 |
| IdsModel | ids: List<Long>;check() 只检查列表非空,不验证元素、重复或对象存在性 |
| ItemCountModel | name、count: Integer;sum(a,b) 相加数量并保留 a 的名称,不检查名称一致 |
| Item2CountModel | name1、name2、count: Integer |
| EntityCountModel<T> | item、count: Integer |
| ItemLongCountModel | item: Long、count: Integer;名称中的 Long 指 item,不是 count |
| StatsModel<T> | x: List<String>、y: List<T>,不自动校验两组长度一致 |
| ChildrenItemModel<I,C> | item、children,可追加子项或按业务键合并多组数据 |
这些 check() 不是 Bean Validation 注解,不会因为 Controller 使用 @Valid 就自动运行。计数模型接收 Long 的构造重载会转为 Integer,没有溢出检查;数量较大时应选用应用自己的长整型统计模型。
合并父项和子项
ChildrenItemModel.merge(groups, keyFunction, mergeFunction) 按父项业务键合并父对象并拼接子列表;四参数重载还可以处理合并后的子列表,例如去重或排序。默认不会去重,也不保证父项输出顺序。
构造 new ChildrenItemModel<>(item) 时会初始化空子列表;无参构造不初始化 children,调用 add 前需先设置可变列表。合并方法同样要求调用方提供完整的非空输入列表、父项和子列表。
建立列表索引
com.own.component.base.model.AbstractListMapModel.Object<T,K> 可由列表和键提取函数建立索引,提供 get(key)、keys()、containsKey(key)、put、putAll。同键保留首个索引对象,追加时原列表仍会收集新项,二者可能含有不同数量的数据。
它保留原列表引用,不复制或封装为不可变快照;需要追加时传入可变列表,不应直接用 List.of(...)。外部直接修改原列表不会自动重建索引,Object.list(key) 当前返回 null,也不是一对多查询。另一个内部 List 实现不是 public,不作为外部公共接入入口。
更新和上传结果
com.own.component.base.model.UpdateModel<T> 保存 flag、data、message。isSuccess() 将空标志视为失败;convertList() 将当前 data 包装为单元素列表,即使 data 为 null 也会保留该元素。传给 R.success(updateModel) 的转换限制见返回统一模型。
UploadModel 包含 fileName、fileOldName、filePath、filePosition、fileType、fileSize(字节)、fileMd5,以及 others、convert、convertId 转换结果字段。它只描述文件信息,不执行上传、转换或生成访问权限;需要实际文件业务时接入资源文件模块。
扩展按环境读取的参数
BaseParameterUtil 是抽象类,不是已经可用的配置中心。应用提供具体存储实现并注册为 Spring Bean,至少实现 get(key, env) 和 set(key, value, env);不带 env 的重载使用字段 env,默认 refresh() 不执行操作。
env 通过 ${spring.profiles.active:dev} 注入。未设置该属性时为 dev;多个 profile 配置会按该属性的字符串值传入,并不会按环境逐项遍历。手动 new 对象时没有 Spring 属性注入,需要显式设置 env。
类中 PREFIX="config:" 只是供子类使用的常量,基础方法不会自动把它拼到 key 上,也不会自动接入 Store、文件或远端配置服务。具体键格式、持久化、刷新和并发规则由实现类定义。