杰瑞科技汇

java request 修改参数

核心概念

在 HTTP 请求中,参数通常存在于以下几个地方:

java request 修改参数-图1
(图片来源网络,侵删)
  1. 请求体: 主要用于 POST, PUT, PATCH 等请求,格式通常是 application/x-www-form-urlencodedapplication/json
  2. 查询字符串: 出现在 URL 的 之后,主要用于 GET 请求,但也可用于其他方法,格式是 key1=value1&key2=value2
  3. 请求头: 包含元数据,如 Content-Type, Authorization 等,虽然不是传统意义上的“参数”,但有时也需要修改。

修改参数的核心思路是:获取到请求对象 -> 修改其属性 -> 将修改后的请求继续传递下去


Servlet 原生 API

在传统的 Servlet 环境中,修改参数比较麻烦,因为 HttpServletRequest 对象是只读的(immutable),你不能直接修改它,常见的解决方案是创建一个请求包装类

修改查询字符串参数

查询字符串参数是通过 request.getParameter() 获取的,但这个方法底层依赖于解析后的 getParameterMap(),而这个 Map 通常是不可变的,我们只能包装 HttpServletRequest 并重写其方法。

步骤:

java request 修改参数-图2
(图片来源网络,侵删)
  1. 创建一个 HttpServletRequest 的包装类
  2. 重写 getParameter(), getParameterMap(), getParameterNames() 等方法。
  3. 在过滤器 中使用这个包装类

示例代码:

请求包装类 (ModifiableRequestWrapper.java)

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.util.*;
public class ModifiableRequestWrapper extends HttpServletRequestWrapper {
    private final Map<String, String[]> modifiableParameters;
    public ModifiableRequestWrapper(HttpServletRequest request) {
        super(request);
        // 将原始参数深拷贝到一个新的可修改的Map中
        this.modifiableParameters = new HashMap<>(request.getParameterMap());
    }
    /**
     * 添加或修改一个参数
     * @param name  参数名
     * @param value 参数值
     */
    public void addParameter(String name, String value) {
        modifiableParameters.put(name, new String[]{value});
    }
    /**
     * 移除一个参数
     * @param name 参数名
     */
    public void removeParameter(String name) {
        modifiableParameters.remove(name);
    }
    @Override
    public String getParameter(String name) {
        String[] values = modifiableParameters.get(name);
        return values != null ? values[0] : null;
    }
    @Override
    public Map<String, String[]> getParameterMap() {
        // 返回一个不可修改的视图,以防止外部代码修改内部的Map
        return Collections.unmodifiableMap(modifiableParameters);
    }
    @Override
    public Enumeration<String> getParameterNames() {
        return Collections.enumeration(modifiableParameters.keySet());
    }
    @Override
    public String[] getParameterValues(String name) {
        return modifiableParameters.get(name);
    }
}

过滤器 (ParameterModifyFilter.java)

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@WebFilter("/*") // 过滤所有请求
public class ParameterModifyFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 将原始请求转换为我们的包装类
        ModifiableRequestWrapper wrappedRequest = new ModifiableRequestWrapper((HttpServletRequest) request);
        // --- 在这里修改参数 ---
        // 示例:将所有名为 "oldName" 的参数修改为 "newName"
        String[] oldValues = wrappedRequest.getParameterValues("oldName");
        if (oldValues != null) {
            wrappedRequest.removeParameter("oldName");
            for (String value : oldValues) {
                wrappedRequest.addParameter("newName", value);
            }
        }
        // 示例:添加一个固定参数
        wrappedRequest.addParameter("source", "web-filter");
        // 将包装后的请求传递给下一个过滤器或目标资源
        chain.doFilter(wrappedRequest, response);
    }
    // ... init, destroy 方法 ...
}

修改请求体

修改请求体比修改查询字符串更复杂,因为请求体只能被读取一次,你需要同样使用包装类,并重写 getInputStream()getReader() 方法,使其返回一个包含修改后内容的流。

java request 修改参数-图3
(图片来源网络,侵删)

步骤:

  1. 创建 HttpServletRequestWrapper
  2. 在构造函数中读取并缓存原始请求体。
  3. 根据你的需求(修改 JSON)解析缓存的内容。
  4. 重写 getInputStream()getReader(),返回一个包含修改后内容的新输入流。

示例(修改 JSON 请求体):

import com.fasterxml.jackson.databind.ObjectMapper;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import java.io.*;
public class JsonBodyModifyWrapper extends HttpServletRequestWrapper {
    private final String modifiedBody;
    public JsonBodyModifyWrapper(HttpServletRequest request) throws IOException {
        super(request);
        // 1. 读取原始请求体
        String originalBody = getBodyAsString(request);
        // 2. 修改 JSON 内容 (这里使用 Jackson)
        ObjectMapper mapper = new ObjectMapper();
        Map<String, Object> payload = mapper.readValue(originalBody, Map.class);
        // 示例:给 JSON 添加一个新字段
        payload.put("processedBy", "java-wrapper");
        // 示例:修改一个现有字段
        if (payload.containsKey("username")) {
            payload.put("username", "modified_" + payload.get("username"));
        }
        // 3. 将修改后的内容写回
        this.modifiedBody = mapper.writeValueAsString(payload);
    }
    @Override
    public ServletInputStream getInputStream() throws IOException {
        final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(modifiedBody.getBytes("UTF-8"));
        return new ServletInputStream() {
            @Override
            public boolean isFinished() { return byteArrayInputStream.available() == 0; }
            @Override
            public boolean isReady() { return true; }
            @Override
            public void setReadListener(ReadListener readListener) { throw new UnsupportedOperationException(); }
            @Override
            public int read() throws IOException { return byteArrayInputStream.read(); }
        };
    }
    @Override
    public BufferedReader getReader() throws IOException {
        return new BufferedReader(new InputStreamReader(this.getInputStream(), "UTF-8"));
    }
    // 辅助方法:从请求中读取字符串
    private String getBodyAsString(HttpServletRequest request) throws IOException {
        StringBuilder stringBuilder = new StringBuilder();
        BufferedReader bufferedReader = null;
        try (InputStream inputStream = request.getInputStream()) {
            if (inputStream != null) {
                bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                char[] charBuffer = new char[128];
                int bytesRead = -1;
                while ((bytesRead = bufferedReader.read(charBuffer)) > 0) {
                    stringBuilder.append(charBuffer, 0, bytesRead);
                }
            }
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ex) {
                    // 忽略
                }
            }
        }
        return stringBuilder.toString();
    }
}

Spring Boot / Spring MVC

Spring Boot 框架提供了更简洁、更强大的方式来处理请求,特别是使用 HandlerInterceptor(拦截器)

使用 HandlerInterceptor 修改请求参数

拦截器是处理请求和响应的理想选择,它比 Servlet 过滤器更贴近业务逻辑。

步骤:

  1. 创建一个 HandlerInterceptor 实现类
  2. 实现 preHandle 方法,这个方法在控制器方法执行之前被调用,是修改请求参数的最佳位置。
  3. 将拦截器注册到 Spring MVC 中

示例代码:

拦截器 (ParameterModifyInterceptor.java)

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Map;
@Component // 标记为Spring组件
public class ParameterModifyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 获取所有参数的Map
        Map<String, String[]> parameterMap = request.getParameterMap();
        // 示例:如果请求中有 "token" 参数,则添加一个新的 "auth" 参数
        if (parameterMap.containsKey("token")) {
            String token = request.getParameter("token");
            // 注意:直接修改 request.getParameterMap() 可能无效,因为Map可能是不可变的
            // 更好的方式是使用 HttpServletRequestWrapper (见下方完整示例)
            // 这里仅作概念演示
            System.out.println("Found token: " + token + ". Will add auth header.");
            // 实际修改参数需要包装 request,下面会给出完整例子
        }
        // 返回 true 表示继续流程,false 表示中断流程
        return true;
    }
}

注册拦截器

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
    @Autowired
    private ParameterModifyInterceptor parameterModifyInterceptor;
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(parameterModifyInterceptor)
                .addPathPatterns("/**"); // 拦截所有路径
    }
}

结合 HttpServletRequestWrapper 的完整 Spring 示例

这是在 Spring 中最健壮的实现方式,结合了拦截器和请求包装。

// 1. 拦截器
@Component
public class AdvancedParameterInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 使用包装类来处理
        if (request instanceof ContentCachingRequestWrapper) {
            // 如果已经有包装,直接使用
            return true;
        }
        return true;
    }
    @Override
    public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 确保流被关闭,以便后续读取
        if (request instanceof ContentCachingRequestWrapper) {
            ((ContentCachingRequestWrapper) request).getContentAsByteArray();
        }
    }
}
// 2. 在配置类中应用包装器和拦截器
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new AdvancedParameterInterceptor())
                .addPathPatterns("/**");
    }
    @Bean
    public FilterRegistrationBean<OncePerRequestFilter> contentCachingFilter() {
        FilterRegistrationBean<OncePerRequestFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(new OncePerRequestFilter() {
            @Override
            protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
                // 使用Spring提供的ContentCachingRequestWrapper来缓存请求体
                ContentCachingRequestWrapper cachingRequest = new ContentCachingRequestWrapper(request);
                filterChain.doFilter(cachingRequest, response);
            }
        });
        registrationBean.addUrlPatterns("/*");
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }
}
// 3. Controller
@RestController
@RequestMapping("/api")
public class MyController {
    @GetMapping("/data")
    public String getData(@RequestParam String id, @RequestParam(required = false) String newName) {
        System.out.println("Controller received - id: " + id + ", newName: " + newName);
        return "Processed id: " + id;
    }
}

注意:Spring 提供了 ContentCachingRequestWrapper,它是一个非常有用的 Servlet 请求包装器,可以缓存请求体,让你多次读取它,上面的例子展示了如何结合使用它。


使用 HTTP 客户端发送请求

如果你不是在服务端修改收到的请求,而是在客户端(如调用其他微服务)时修改要发送的请求,那就简单多了。

使用 RestTemplate (旧版)

import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.Collections;
public class RestTemplateExample {
    public static void main(String[] args) {
        RestTemplate restTemplate = new RestTemplate();
        // 1. 准备URL和参数
        String url = "http://example.com/api/data";
        // 修改查询参数
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url)
                .queryParam("id", "123") // 设置或覆盖 id 参数
                .queryParam("from", "my-client"); // 添加新参数
        // 2. 准备请求体 (如果是 POST/PUT)
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
        body.add("original_key", "original_value");
        body.add("modified_key", "new_value"); // 修改或添加请求体参数
        // 3. 设置请求头
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        headers.set("Authorization", "Bearer token123");
        HttpEntity<MultiValueMap<String, String>> entity = new HttpEntity<>(body, headers);
        // 4. 发送请求
        ResponseEntity<String> response = restTemplate.exchange(
                builder.build().toUri(), 
                HttpMethod.POST, 
                entity, 
                String.class);
        System.out.println(response.getBody());
    }
}

使用 WebClient (新版,推荐)

WebClient 是 Spring 5 引入的响应式 HTTP 客户端,更加灵活。

import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;
public class WebClientExample {
    public static void main(String[] args) {
        WebClient webClient = WebClient.create("http://example.com");
        // 修改查询参数和请求体
        Mono<String> response = webClient.post()
                .uri(uriBuilder -> uriBuilder
                        .path("/api/data")
                        .queryParam("id", "456") // 修改查询参数
                        .queryParam("source", "web-client") // 添加查询参数
                        .build())
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue("{\"original_field\": \"value\", \"updated_field\": \"new_value\"}") // 直接修改JSON请求体
                .retrieve()
                .bodyToMono(String.class);
        // 订阅并打印结果
        response.subscribe(System.out::println);
    }
}
场景 核心工具/方法 关键点
服务端修改请求
Servlet 环境 HttpServletRequestWrapper + Filter 必须包装请求对象,因为原生 request 是只读的。
Spring Boot HandlerInterceptor + HttpServletRequestWrapper (如 ContentCachingRequestWrapper) 拦截器是切入点,包装是实现修改的手段。
客户端发送请求
RestTemplate UriComponentsBuilder (查询参数), HttpEntity (请求体/头) 直接构建 UriComponentsHttpEntity 来设置你想要的任何参数。
WebClient uri() (查询参数), bodyValue() (请求体) 在链式调用中,通过 uri() builder 和 bodyValue() 方法轻松修改。

希望这个详细的解释能帮助你解决在 Java 中修改请求参数的问题!

分享:
扫描分享到社交APP
上一篇
下一篇