Spring Boot Filter转换请求格式并添加Form-Data参数失败求助
JSON请求转multipart/form-data并添加固定参数的解决思路
问题核心
- 需求:将
Content-Type: application/json的请求在Filter中转为multipart/form-data,并添加硬编码参数scope=read - 报错原因:直接将
ContentCachingRequestWrapper强转为MultipartHttpServletRequest,两者无继承/实现关系,类型转换失败
解决步骤
1. 理解类型转换失败的本质
ContentCachingRequestWrapper只是Spring提供的普通HTTP请求缓存包装类,仅用于缓存请求体以便重复读取;而MultipartHttpServletRequest是Spring专门处理multipart请求的接口,只有当请求本身是multipart类型且被MultipartResolver处理后,才会生成该类型实例。要实现JSON转multipart,必须自定义符合要求的请求包装类。
2. 自定义Multipart请求包装类
继承AbstractMultipartHttpServletRequest,将原JSON请求体包装为MultipartFile,同时注入自定义参数:
public class CustomMultipartRequestWrapper extends AbstractMultipartHttpServletRequest { private final Map<String, String[]> customParams; private final Map<String, MultipartFile> multipartFiles; public CustomMultipartRequestWrapper(HttpServletRequest request, String jsonBody) throws IOException { super(request); // 复制原请求参数,添加自定义参数 customParams = new HashMap<>(request.getParameterMap()); customParams.put("scope", new String[]{"read"}); // 将原JSON请求体包装为MultipartFile multipartFiles = new HashMap<>(); multipartFiles.put("originalJson", new MockMultipartFile( "originalJson", "originalJson.json", "application/json", jsonBody.getBytes(request.getCharacterEncoding()) )); } // 重写参数相关方法,返回自定义参数集合 @Override public Map<String, String[]> getParameterMap() { return customParams; } @Override public String getParameter(String name) { String[] values = customParams.get(name); return values != null && values.length > 0 ? values[0] : null; } @Override public Enumeration<String> getParameterNames() { return Collections.enumeration(customParams.keySet()); } @Override public String[] getParameterValues(String name) { return customParams.get(name); } // 重写文件相关方法,返回包装后的JSON文件 @Override public Map<String, MultipartFile> getFileMap() { return multipartFiles; } @Override public Iterator<String> getFileNames() { return multipartFiles.keySet().iterator(); } @Override public MultipartFile getFile(String name) { return multipartFiles.get(name); } @Override public List<MultipartFile> getFiles(String name) { MultipartFile file = multipartFiles.get(name); return file != null ? Collections.singletonList(file) : Collections.emptyList(); } }
3. 修改Filter逻辑
在Filter中判断请求类型,对JSON请求进行转换,同时修改Content-Type:
@Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; HttpServletRequest requestToUse = httpServletRequest; String contentType = httpServletRequest.getContentType(); // 仅对JSON请求进行转换 if (contentType != null && contentType.contains("application/json")) { // 缓存并读取原请求体 ContentCachingRequestWrapper cachedRequest = new ContentCachingRequestWrapper(httpServletRequest); String jsonBody = getStringValue(cachedRequest.getContentAsByteArray(), httpServletRequest.getCharacterEncoding()); // 构造自定义Multipart请求 CustomMultipartRequestWrapper multipartRequest = new CustomMultipartRequestWrapper(httpServletRequest, jsonBody); // 修改Content-Type为multipart/form-data multipartRequest.setContentType("multipart/form-data"); requestToUse = multipartRequest; } else { // 非JSON请求使用缓存包装类用于日志 requestToUse = new ContentCachingRequestWrapper(httpServletRequest); } ContentCachingResponseWrapper responseWrapper = new ContentCachingResponseWrapper(httpServletResponse); filterChain.doFilter(requestToUse, responseWrapper); // 原有日志逻辑保持不变 String responseBody = getStringValue(responseWrapper.getContentAsByteArray(), servletResponse.getCharacterEncoding()); generateTokenUriLog(responseBody, httpServletRequest, httpServletResponse); responseWrapper.copyBodyToResponse(); }
4. 关键注意事项
- 确保Spring容器中配置了
MultipartResolver(如StandardServletMultipartResolver),否则后续处理multipart请求会报错。 - 避免对已为multipart类型的请求重复转换,防止逻辑冲突。
- 使用
ContentCachingRequestWrapper缓存请求体,确保原请求体可被多次读取(HTTP请求体默认只能读取一次)。
内容的提问来源于stack exchange,提问作者RedBlue
相关产品推荐
相关产品推荐

