You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:10:07