【每日鲜蘑】异常`Not enough variable values available to expand 'phone'`

10,014 阅读1分钟

这是一个很常见的非法参数异常。

报错信息

java.lang.IllegalArgumentException: Not enough variable values available to expand 'phone'
        at org.springframework.web.util.UriComponents$VarArgsTemplateVariables.getValue(UriComponents.java:367)
        at org.springframework.web.util.HierarchicalUriComponents$QueryUriTemplateVariables.getValue(HierarchicalUriComponents.java:1055)
        at org.springframework.web.util.UriComponents.expandUriComponent(UriComponents.java:262)
        at org.springframework.web.util.HierarchicalUriComponents.lambda$expandQueryParams$5(HierarchicalUriComponents.java:443)
        at java.util.Map.forEach(Map.java:630)
        at org.springframework.web.util.HierarchicalUriComponents.expandQueryParams(HierarchicalUriComponents.java:439)
        at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:429)
        at org.springframework.web.util.HierarchicalUriComponents.expandInternal(HierarchicalUriComponents.java:51)
        at org.springframework.web.util.UriComponents.expand(UriComponents.java:172)
        at org.springframework.web.util.DefaultUriBuilderFactory$DefaultUriBuilder.build(DefaultUriBuilderFactory.java:376)
        at org.springframework.web.util.DefaultUriBuilderFactory.expand(DefaultUriBuilderFactory.java:202)
        at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:669)
        at org.springframework.web.client.RestTemplate.postForObject(RestTemplate.java:414)

解析

这是个占位符的问题。

RestTemplate在请求的 url 中存在{phone}这个值,默认到参数中找对应的值做替换。

解析 url 中存在的特殊值

正则表达式"\\{([^/]+?)\\}"命中了{phone},那么程序回去寻找参数中的phone进行替换。

    private static final Pattern NAMES_PATTERN = Pattern.compile("\\{([^/]+?)\\}");

    @Nullable
    static String expandUriComponent(@Nullable String source, UriTemplateVariables uriVariables,
            @Nullable UnaryOperator<String> encoder) {

        if (source == null) {
            return null;
        }
        if (source.indexOf('{') == -1) {
            return source;
        }
        if (source.indexOf(':') != -1) {
            source = sanitizeSource(source);
        }
        Matcher matcher = NAMES_PATTERN.matcher(source);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            String match = matcher.group(1);
            String varName = getVariableName(match);
            Object varValue = uriVariables.getValue(varName);
            if (UriTemplateVariables.SKIP_VALUE.equals(varValue)) {
                continue;
            }
            String formatted = getVariableValueAsString(varValue);
            formatted = encoder != null ? encoder.apply(formatted) : Matcher.quoteReplacement(formatted);
            matcher.appendReplacement(sb, formatted);
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

从 uriVariables 中寻值

如果找不到 phone,则会提示异常IllegalArgumentException

    /**
     * URI template variables backed by a variable argument array.
     */
    private static class VarArgsTemplateVariables implements UriTemplateVariables {

        private final Iterator<Object> valueIterator;

        public VarArgsTemplateVariables(Object... uriVariableValues) {
            this.valueIterator = Arrays.asList(uriVariableValues).iterator();
        }

        @Override
        @Nullable
        public Object getValue(@Nullable String name) {
            if (!this.valueIterator.hasNext()) {
                throw new IllegalArgumentException("Not enough variable values available to expand '" + name + "'");
            }
            return this.valueIterator.next();
        }
    }

解决

我们这边抛出异常,是因为有部分历史数据中用户的手机号中设置成138${phone}这种值,在写入的时候没有做合法性校验。

总结

URL把花括号{}中的内容当成了占位符,而又没有传入占位符对应的值,才导致报错。