You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Spring Boot中参数编解码最佳方式及@RequestParam全局解码方案咨询

问题分析与解决方案

你遇到的核心问题是当请求参数值包含&这类URL特殊字符时,Spring的@RequestParam会把它当作参数分隔符,导致参数值被截断,而且手动在每个方法里解码太繁琐。另外你发现curl调用正常但Java代码调用异常,这其实和请求发送时的编码逻辑有关,下面一步步给你理清楚:

一、为什么会出现参数截断?

当你传递name=abc&def&id=123这种参数时,URL解析器会默认把&当作参数分隔符,所以会识别成三个参数:name=abcdef(无值)、id=123,自然name的值就只有abc了。

至于curl调用正常、Java代码调用异常的差异:

  • curl会自动对URL中的特殊字符进行编码(比如把&转成%26),后端收到的是编码后的字符串,Spring自动解码后就能得到正确的abc&def
  • 你的Java代码里直接拼接了ello%26test,但RestTemplate在发送请求时会自动对URL参数进行二次编码,把%26转成%2526,后端收到的就是ello%26test而非解码后的ello&test,这就是异常的原因。

二、Spring是否提供全局解码@RequestParam的方法?

当然有!完全不用在每个控制器方法里重复写解码逻辑,推荐两种全局处理方式:

1. 自定义参数解析器(通用所有Spring Web环境)

自定义一个HandlerMethodArgumentResolver,对所有标注@RequestParam的参数统一解码:

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;

public class RequestParamDecoderResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // 只处理带有@RequestParam注解的参数
        return parameter.hasParameterAnnotation(RequestParam.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        RequestParam requestParam = parameter.getParameterAnnotation(RequestParam.class);
        String paramName = requestParam.name().isEmpty() ? requestParam.value() : requestParam.name();
        String paramValue = webRequest.getParameter(paramName);
        
        if (paramValue != null) {
            // 统一用UTF-8解码
            return URLDecoder.decode(paramValue, StandardCharsets.UTF_8.name());
        }
        // 处理参数为空的情况:如果是必填返回null(会触发Spring的参数校验),否则返回默认值
        return requestParam.required() ? null : requestParam.defaultValue();
    }
}

然后在配置类里注册这个解析器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(new RequestParamDecoderResolver());
    }
}

这样所有@RequestParam参数都会自动完成解码,不用再手动写解码逻辑。

2. 修改Tomcat容器编码配置(仅适用于Tomcat部署)

如果你的应用部署在Tomcat上,可以修改conf/server.xml里的Connector配置,强制Tomcat对URI参数统一解码:

<Connector port="8080" protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443"
           URIEncoding="UTF-8"
           useBodyEncodingForURI="true"/>

这个配置会让Tomcat提前对请求URI中的参数做UTF-8解码,Spring拿到的就是直接可用的参数值。

三、使用@RequestParam时是否需要手动编解码?

分场景来看:

  • 客户端发送请求时:必须对参数值中的特殊字符(&=%等)做URL编码,否则会被解析器当作参数分隔符,导致参数截断;
  • 后端接收时:正常情况下Spring已经自动完成了解码,只有遇到客户端编码不规范、或者需要自定义解码规则的特殊场景,才需要手动处理,或者用上面的全局配置统一解决。

另外针对你测试代码的问题,给你修正一下:
不要手动拼接编码后的参数,直接传递原始字符串,让RestTemplate自动编码:

import org.springframework.web.client.RestTemplate;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.GetMapping;

@GetMapping(value = "/request")
public String request() {
    String url = "http://127.0.0.1:8080/example";
    RestTemplate restTemplate = new RestTemplate();
    // 用MultiValueMap传递参数,RestTemplate会自动编码特殊字符
    MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
    params.add("param", "ello&test");
    return restTemplate.postForObject(url, params, String.class);
}

这样后端收到的param值就是ello&test,而不是编码后的字符串。

内容的提问来源于stack exchange,提问作者Rebecca

火山引擎 最新活动