Thymeleaf中Map查找为何始终返回true?
我在Thymeleaf代码中尝试用param变量获取请求参数,编写了如下代码:
<a th:if="${#maps.containsKey(param, 'location')}" th:href="@{my_endpoint(pageNo=${i},location=${param.location})}" th:text="${i}" class="Some classes">stat+1</a>
问题是,即便请求中不存在location参数,containsKey()也始终返回true。
为了排查问题,我做了调试:先输出param的所有键,再用一个完全不存在的随机字符串kjkakajkdkdkhaha-1做存在性检查,结果所有检查都返回true。调试代码如下:
<div th:each="map : ${param.keySet()}"> <p th:text="' key ' + ${map}"></p> </div> <div th:if="${param.containsKey('kjkakajkdkdkhaha-1')}"> <p th:text="'kjkakajkdkdkhaha-1 key check ' + ${#bools.isTrue((param.containsKey('kjkakajkdkdkhaha-1')))}"></p> <p th:text="'kjkakajkdkdkhaha-1 key check ' + ${param.containsKey('kjkakajkdkdkhaha-1')}"></p> <p th:text="'kjkakajkdkdkhaha-1 key check ' + ${#maps.containsKey(param, 'kjkakajkdkdkhaha-1')}"></p> </div>
Chrome开发者工具显示渲染后的元素:
<div> <p> key name</p> </div> <div> <p> key continue</p> </div> <div> <p>kjkakajkdkdkhaha-1 key check true</p> <p>kjkakajkdkdkhaha-1 key check true</p> <p>kjkakajkdkdkhaha-1 key check true</p> </div>
从渲染结果能看到,param的实际键只有name和continue,但查询不存在的键时,所有检查方式都返回true,这不符合预期。请问这是为什么?
问题原因及解决办法
核心原因
Thymeleaf中的param并非标准Java Map,而是WebRequestParamsVariablesMap的实例。它的containsKey()方法依赖于Servlet请求的getParameterValues()方法返回值是否为null,但如果你的请求对象被包装(比如Spring的缓存请求包装类)、存在隐性参数(如表单隐藏域、过滤器自动添加的参数),或者使用的Thymeleaf版本存在逻辑bug,都可能导致该方法误返回true。
从你的调试结果看,param.keySet()显示的键与containsKey()结果矛盾,大概率是containsKey()的底层逻辑没有正确读取实际请求参数集合。
解决办法
放弃使用containsKey(),直接通过参数值的存在性来判断:
方式1:检查参数是否存在且有值
<a th:if="${param.location != null and #arrays.length(param.location) > 0}" th:href="@{my_endpoint(pageNo=${i},location=${param.location[0]})}" th:text="${i}" class="Some classes">stat+1</a>
方式2:简化判断(Thymeleaf会自动处理null和空数组)
<a th:if="${param.location}" th:href="@{my_endpoint(pageNo=${i},location=${param.location[0]})}" th:text="${i}" class="Some classes">stat+1</a>
额外说明
param对象返回的参数值是字符串数组(支持多值参数),所以如果要获取单个参数值,记得用param.location[0]来取第一个元素。
内容的提问来源于stack exchange,提问作者Mukit09

