使用GlassFish Jersey测试multipart/form-data单元遇错,求解决方法
问题:Jersey Multipart单元测试抛出“Entity instance does not contain the unconverted content”异常
场景说明
我在项目中通过REST API接收Map<String, List<FormDataBodyPart>>类型的multipart表单数据,传入manager类后用以下代码读取JSON内容:
JsonNode data = objectMapper.readTree(requestFields.get(REQUEST).get(0).getValueAs(InputStream.class));
生产环境通过Postman调用完全正常,但编写单元测试时触发了如下异常:
异常栈信息
java.lang.IllegalStateException: Entity instance does not contain the unconverted content. at org.glassfish.jersey.media.multipart.BodyPart.getEntityAs(BodyPart.java:267) at org.glassfish.jersey.media.multipart.FormDataBodyPart.getValueAs(FormDataBodyPart.java:291) at com.confluxsys.iam.adc.core.impl.request.RequestManagerImpl.createRequest(RequestManagerImpl.java:178) at com.confluxsys.iam.adc.util.request.TestRequestManagerV2.createRequestV2(TestRequestManagerV2.java:154)
测试类代码
String data = "{\"menu\":{\"id\":\"file\",\"value\":\"File\",\"popup\":{\"menuitem\":[{\"value\":\"New\",\"onclick\":\"CreateNewDoc()\"},{\"value\":\"Open\",\"onclick\":\"OpenDoc()\"},{\"value\":\"Close\",\"onclick\":\"CloseDoc()\"}]}}}"; InputStream jsonStream; try (FormDataMultiPart formDataMultiPart = new FormDataMultiPart()) { FormDataBodyPart form1 = new FormDataBodyPart(); jsonStream = new ByteArrayInputStream(config.getBytes()); form1.setMediaType(MediaType.APPLICATION_JSON_TYPE); form1.setValue(MediaType.APPLICATION_JSON_TYPE, jsonStream); form1.setName("part-config"); form1.getHeaders().add("Content-Length", String.valueOf(config.getBytes().length)); formDataMultiPart.bodyPart(form1); requestManager.createRequest(formDataMultiPart.getFields()); }
主类核心代码
public void createRequest(Map<String, List<FormDataBodyPart>> requestFields) throws SystemFailureException, com.confluxsys.iap.config.util.ObjectNotFoundException { if (!requestFields.containsKey(REQUEST)) { logger.error("Request json is not passed in the request payload"); throw new IllegalArgumentException("The 'request' body part is missing in the payload."); } try { ObjectMapper objectMapper = new ObjectMapper(); // 假设request字段始终存在且取第一个元素 JsonNode data = objectMapper.readTree(requestFields.get(REQUEST).get(0).getValueAs(InputStream.class)); /* 后续验证逻辑 */ } catch (Exception e) { throw new SystemFailureException("Error occured while creating request .Rolling back all operation .cause : ", e); } }
使用依赖:org.glassfish.jersey 2.35
解决方案
核心原因
生产环境中,Jersey会自动解析HTTP请求并填充FormDataBodyPart的内部状态(包括标记实体已准备好转换)。但单元测试中手动构造FormDataBodyPart时,缺少这一内部状态初始化,导致调用getValueAs()时Jersey认为没有可转换的未处理内容,抛出异常。
修复方案1:使用正确的FormDataBodyPart构造方式
直接通过构造函数传入字段名、内容和媒体类型,让Jersey内部自动处理实体状态:
String config = "{\"menu\":{\"id\":\"file\",\"value\":\"File\",\"popup\":{\"menuitem\":[{\"value\":\"New\",\"onclick\":\"CreateNewDoc()\"},{\"value\":\"Open\",\"onclick\":\"OpenDoc()\"},{\"value\":\"Close\",\"onclick\":\"CloseDoc()\"}]}}}"; try (FormDataMultiPart formDataMultiPart = new FormDataMultiPart()) { // 直接构造,自动处理实体内容 FormDataBodyPart form1 = new FormDataBodyPart("request", config, MediaType.APPLICATION_JSON_TYPE); formDataMultiPart.bodyPart(form1); requestManager.createRequest(formDataMultiPart.getFields()); }
修复方案2:手动设置未转换实体内容
如果必须使用InputStream,需要手动将流包装为UnparsedEntity并设置到BodyPart:
String config = "{\"menu\":{\"id\":\"file\",\"value\":\"File\",\"popup\":{\"menuitem\":[{\"value\":\"New\",\"onclick\":\"CreateNewDoc()\"},{\"value\":\"Open\",\"onclick\":\"OpenDoc()\"},{\"value\":\"Close\",\"onclick\":\"CloseDoc()\"}]}}}"; InputStream jsonStream = new ByteArrayInputStream(config.getBytes()); try (FormDataMultiPart formDataMultiPart = new FormDataMultiPart()) { FormDataBodyPart form1 = new FormDataBodyPart(); form1.setName("request"); form1.setMediaType(MediaType.APPLICATION_JSON_TYPE); // 手动设置未解析的实体内容 form1.setEntity(new UnparsedEntity(jsonStream, MediaType.APPLICATION_JSON_TYPE, null)); formDataMultiPart.bodyPart(form1); requestManager.createRequest(formDataMultiPart.getFields()); }
修复方案3:重构依赖(推荐)
将manager类的输入参数从Map<String, List<FormDataBodyPart>>改为更易测试的类型(如JsonNode或业务DTO),这样单元测试无需构造Multipart组件,直接传入JSON数据即可:
// 修改后的manager方法 public void createRequest(JsonNode requestData) throws SystemFailureException, ObjectNotFoundException { /* 原有验证和业务逻辑 */ } // 控制器层处理Multipart转换 public Response handleRequest(@FormDataParam("request") InputStream requestStream) { JsonNode data = objectMapper.readTree(requestStream); requestManager.createRequest(data); return Response.ok().build(); } // 单元测试示例 @Test public void testCreateRequest() throws Exception { String jsonStr = "{\"menu\":{\"id\":\"file\",\"value\":\"File\",\"popup\":{\"menuitem\":[{\"value\":\"New\",\"onclick\":\"CreateNewDoc()\"}]}}}"; JsonNode requestData = new ObjectMapper().readTree(jsonStr); requestManager.createRequest(requestData); // 断言业务逻辑执行结果 }
内容的提问来源于stack exchange,提问作者Harsha
相关产品推荐
相关产品推荐

