背景:
由于一些网络上面的原因,不能使用nginx,需要自己手动转发请求,涉及HttpServlet中的请求构造以及转发
请求类型:GET,POST
参数类型:json,form
业务:正常业务访问.上传下载,图片查看
其中有趟到以下坑,记录一下,为后来者做一点参考
1.HttpServletResponse设置Content-Type未生效
场景:
在HttpUtil请求获取返回值后,设置返回的contentType,因为通用工具,告诉前端浏览器使用对应的解析器来解析返回值,代码如下
private void dealResponse(HttpServletResponse resp, byte[] data, String contentType) {
resp.setHeader(Constant.CONTENT_TYPE[0], contentType);
// resp.setContentType(contentType);
try (ServletOutputStream out = resp.getOutputStream()) {
if (out != null) {//在关资源前一定要判断是否为空
// IOUtils.write(data, out);
out.write(data);
out.flush();
out.close();
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
问题分析:
参考以下文章,认为HttpServlet设置contentType无效,于是自己定义Controller,使用无返回值方法来解析上面dealResponse的返回值,但结果contentType还是没有生效,后面把data打印出来,参数如下字符串,这居然是字符串,HttpServlet在判断返回值为字符串时,不会再调用contentType设置.所以导致一直没有生效.
最后返回正确的json格式后,在HttpServlet设置contentType是有效的
new String(data) = "{\"msg\":\"\",\"code\":200,\"data\":[{\"createdBy\":null,\"lastUpdatedBy\":null,\"creationDate\":\"2021-10-18 19:22:40\",\"lastUpdateTime\":\"2021-10-18 19:22:40\",\"activeFlag\":null,\"id\":\"f8370a5eafac4c8b929e40090136051c\"}],\"success\":true}"
有以下文章参考
[Bug] HttpServletResponse设置Content-Type未生效
2.从HttpServlet中获取不到附件信息
获取不到任何附件信息,按照StandardMultipartHttpServletRequest中的Collection parts = request.getParts()可以获取到附件,但是获取不到附件之外的参数,所以再继续找,如以下将HttpServletRequest 装换为MultipartHttpServletRequest可以获取到付件并且获取到附件之外的参数
protected ServletRequestVO buildUploadParam(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
log.info(">>>>>>>>>>doUpload()<<<<<<<<<<<");
ServletRequestVO servletRequestVO = new ServletRequestVO(req);
String requestURL = ServersConfig.smrApi + req.getRequestURI();
CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver(req.getSession().getServletContext());
MultipartHttpServletRequest multipartRequest = commonsMultipartResolver.resolveMultipart(req);
List<MultipartFile> multipartFiles = multipartRequest.getFiles("myFiles");
CustomMultipartFile[] mockMultipartFiles = new CustomMultipartFile[multipartFiles.size()];
for (int i=0; i< multipartFiles.size(); i++) {
MultipartFile multipartFile = multipartFiles.get(i);
CustomMultipartFile mockMultipartFile = new CustomMultipartFile(ContentType.APPLICATION_OCTET_STREAM.toString(), multipartFile.getOriginalFilename(), multipartFile.getContentType(),multipartFile.getBytes());
mockMultipartFiles[i] = mockMultipartFile;
}
Map<String, Object> bodyParamMap = new HashMap<>();
Enumeration<String> paramIte = multipartRequest.getParameterNames();
while (paramIte.hasMoreElements()) {
String key = paramIte.nextElement();
bodyParamMap.put(key, multipartRequest.getParameter(key));
}
log.info("bodyParamMap : {}", JSON.toJSONString(bodyParamMap));
Map<String, String> headMap = new HashMap<>();
headMap.put(Constant.CONTENT_TYPE[0], servletRequestVO.getContentType());
servletRequestVO.setFiles(mockMultipartFiles);
servletRequestVO.setBodyParam(JSON.toJSONString(bodyParamMap));
return servletRequestVO;
}
3.从HttpServlet中获取附件使用MultipartFile[]流传到另外一个模块,反序列化后,没有图片信息,以及图片信息之外的其他参数
MockMultipartFile[] mockMultipartFiles = new MockMultipartFile[multipartFiles.size()];
MultipartFile为接口,fastjson不能序列化接口参数,需要换成MultipartFile的实现类类实现文件的传输以及反序列化,pom引入如下
<!-- file upload part -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.3.6</version>
<scope>compile</scope>
</dependency>
4.使用MockMultipartFile[]传到另外一个模块,反序列化后,没有图片信息,以及图片信息之外的其他参数
打印参数,有bytes参数,但是就是没有序列化到MockMultipartFile中,MockMultipartFile[0].isEmpty() = true,说明附件就为空
问题分析:
打印参数如上,发现有bytes参数传过来,说明序列化没有问题,反序列化出问题了,遂找MockMultipartFile实现,报货content字段在内的字段都为final修饰,所以肯定值没有setxxx方法的,
同时注意下面的getBytes()方法,就明白了序列化的bytes是从哪来的,但是MockMultipartFile字段中没有bytes属性,所以反序列化反射找不到字段,自然不会赋值.
解决方案:
一种是自定义类继承MockMultipartFile,增加setContent()方法,但是因为是final修饰的,所以此路不通
另外一种就是新增getContent()方法,有content属性,饭序列化反射肯定可以成功赋值,在CustomMultipartFile序列化和反序列化即可
CustomMultipartFile[] mockMultipartFiles = new CustomMultipartFile[multipartFiles.size()];
public final class CustomMultipartFile extends MockMultipartFile {
public CustomMultipartFile(String name, String originalFilename, String contentType, byte[] content) {
super(name, originalFilename, contentType, content);
}
public byte[] getContent() throws IOException {
return super.getBytes();
}
public byte[] getBytes() throws IOException {
return null;
}
}
4.上传报错UnsupportedOperationException
java.lang.UnsupportedOperationException: Multipart form entity does not implement #getContent()
使用如下方式获取:
HttpEntity httpEntity = entityBuilder.build();
ByteArrayOutputStream out = new ByteArrayOutputStream();
// write content to stream
httpEntity.writeTo(out);
String bodyParam = out.toString();
4.上传报错MultipartException
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
头中设置Content-Type
headMap.put("Content-Type", "multipart/form-data");
4.上传报错FileUploadException
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
在第五步设置头的过程中没有设置boundary导致的,而boundary为两个参数二进制的识别标志位,所以毕传,在创建entityBuilder后即创建boundary
MultipartEntityBuilder entityBuilder = MultipartEntityBuilder.create();
entityBuilder.setCharset(StandardCharsets.UTF_8);
entityBuilder.setBoundary(boundary);