Oracle APEX动态动作报ORA-20987 主键实时校验问题咨询
报错触发原因
这个错误是Oracle APEX的**会话状态保护(Session State Protection, SSP)**安全机制触发的:
- APEX默认会给所有页面项开启会话状态保护,防止用户通过篡改URL、伪造AJAX请求的方式恶意修改受保护的项值。当项的保护级别为需要会话级校验和的档位时,所有修改、提交该项到会话状态的请求,必须携带和当前会话、项值匹配的加密校验和才会被放行。
- 你自定义的动态动作在执行PL/SQL时,配置了提交
P6_COUNTRY_ID到会话状态,但请求没有携带对应的合法校验和,因此被APEX判定为非法请求,直接抛出ORA-20987错误。
注意:不建议通过关闭页面项的会话状态保护来绕过这个报错,会引入参数篡改的安全风险。
现有方案合理性评估
你的实现思路方向是对的,但存在几个明显问题:
- 额外创建隐藏项存储校验结果,增加了不必要的会话状态存储开销,流程冗余。
- PL/SQL块使用
WHEN OTHERS捕获所有异常的写法不严谨:除了主键不存在触发的NO_DATA_FOUND异常外,表权限缺失、字段名写错等其他无关异常也会被判定为「主键不存在」,会掩盖真实运行错误,出问题时连排查方向都找不到,逻辑漏洞很明显。 - 没有适配编辑表单场景:如果页面是用来编辑已有的国家记录,输入当前记录本身的
COUNTRY_ID也会被判定为重复,不符合业务逻辑。 - 自定义动态动作的AJAX请求没有自动处理会话状态校验和,容易触发安全拦截报错。
更简便规范的实现方式
推荐直接用APEX原生的校验能力实现,不需要自定义动态动作、隐藏项,完全不会触发上述安全报错,实现步骤如下:
- 先清理之前创建的冗余配置:删除隐藏项
P6_C_ID_CHECK、自定义的change触发动态动作全套配置。 - 在页面渲染树的「处理」-「校验」节点下新建校验:
- 校验类型选择:
不存在行的SQL查询(No Rows Returned) - 校验SQL语句写为:
SELECT 1 FROM COUNTRY WHERE COUNTRY_ID = :P6_COUNTRY_ID -- 编辑场景下追加以下条件,排除当前正在编辑的记录本身,避免误判 -- 如果你的表单是基于ROWID定位记录,就用:P6_ROWID判断,如果是主键自身定位,调整为对应逻辑 AND (:P6_ROWID IS NULL OR ROWID <> :P6_ROWID)- 关联校验的项选择
P6_COUNTRY_ID,错误提示信息填写「输入的国家ID已存在,请重新输入」。
- 校验类型选择:
- 选中页面项
P6_COUNTRY_ID,在属性面板的「高级」分组下,将**值改变时验证(Validate when Value Changes)**设置为「是」。
配置完成后,用户输入完P6_COUNTRY_ID失焦时,APEX会自动发起带合法校验和的AJAX请求执行校验,校验不通过时会直接在输入项下方显示错误提示,不需要手写任何提示逻辑、会话状态处理代码,是APEX官方推荐的标准实现方式,不用自己处理AJAX、校验和、提示位置这些杂事,兼容性和安全性都比自定义动态动作靠谱,维护起来也简单。
如果确实需要保留自定义动态动作的实现逻辑,不要关闭项的会话状态保护,只需要在执行PL/SQL的动作属性中,找到「会话状态」分组下的**发送校验和(Send Checksum)**选项开启即可,同时需要把PL/SQL里的WHEN OTHERS改为显式捕获NO_DATA_FOUND异常,补全编辑场景的自身记录排除逻辑。
内容的提问来源于stack exchange,提问作者dendiie
相关产品推荐
相关产品推荐

