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

RestTemplate调用时如何处理produces中的多种响应媒体类型

问题描述

我通过rest template调用某API,该API会根据id返回图片byte[],当出现HTTP NOT FOUND(对应id不存在)时,返回JSON格式的错误对象。因特殊原因未使用全局异常处理器,直接在当前端点处理异常。成功场景下可正常获取图片,但当API返回JSON错误响应时,会抛出如下异常:

org.springframework.http.converter.HttpMessageNotWritableException: No converter for [class org.json.simple.JSONObject] with preset Content-Type 'null']

以下是使用rest template调用API的代码:

@GetMapping(value="api/image/{id}" , produces = { MediaType.IMAGE_JPEG_VALUE, MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody ResponseEntity<Object> api_image_customer(@PathVariable(name = "id")String id){

    String accessToken = getCrmAccessToken();
    
    RestTemplate restTemplate = new RestTemplate();

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
    headers.add("Authorization", "Bearer "+accessToken);
    ResponseEntity<byte[]> response = null;
    HttpEntity formEntity = new HttpEntity(null, headers);

    try {
        response = restTemplate.exchange("http://172.18.5.19:8082/api/image/"+id, HttpMethod.GET,
                formEntity, byte[].class);

        if(response!=null && response.getStatusCode().is2xxSuccessful()) {
            byte[] imageBytes = response.getBody();
            return new ResponseEntity<>(imageBytes, HttpStatus.OK);
            
        }
        else if(response!=null && !response.getStatusCode().is2xxSuccessful()){

            return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
            
        }
        else
        {
            return new ResponseEntity<>("ERROR", HttpStatus.INTERNAL_SERVER_ERROR);
            
        }
    }
    catch(HttpClientErrorException ex) {
        String msg = ex.getResponseBodyAsString();
        System.out.println(msg+" !!!!"); // this is getting printed as json string.
        JSONParser parser =new JSONParser();
        JSONObject obj = null;
        try {
            obj = (JSONObject)parser.parse(msg);

        }catch(Exception ex_) {
            ex_.printStackTrace();
            return new ResponseEntity<>("error converting json", HttpStatus.INTERNAL_SERVER_ERROR);
            
        }
        return new ResponseEntity<>(obj, HttpStatus.INTERNAL_SERVER_ERROR);
        
    }
}

我原本以为@GetMapping的produces属性配置多种MediaType即可处理图片和JSON两种响应,但并未生效。请问是否有办法在此处管理两种响应类型,比如动态修改响应的content-type?


解决方案

问题根源在于返回JSON错误时未指定正确的Content-Type,且Spring默认消息转换器对org.json.simple.JSONObject支持不足。可以通过以下方式修复:

1. 动态设置响应Content-Type

在返回不同类型结果时,手动为响应头指定对应的媒体类型:

  • 返回图片时,设置MediaType.IMAGE_JPEG
  • 返回JSON错误时,设置MediaType.APPLICATION_JSON

修改核心返回逻辑:

// 成功返回图片
if(response!=null && response.getStatusCode().is2xxSuccessful()) {
    byte[] imageBytes = response.getBody();
    HttpHeaders imageHeaders = new HttpHeaders();
    imageHeaders.setContentType(MediaType.IMAGE_JPEG);
    return new ResponseEntity<>(imageBytes, imageHeaders, HttpStatus.OK);
}

2. 替换JSONObject为Spring兼容类型

避免使用org.json.simple.JSONObject,改用Spring默认支持的Map或自定义错误DTO类,减少序列化异常:

catch(HttpClientErrorException ex) {
    String msg = ex.getResponseBodyAsString();
    HttpHeaders jsonHeaders = new HttpHeaders();
    jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
    
    try {
        // 用Jackson将JSON字符串转为Map
        ObjectMapper objectMapper = new ObjectMapper();
        Map<String, Object> errorMap = objectMapper.readValue(msg, Map.class);
        return new ResponseEntity<>(errorMap, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
    } catch (Exception ex_) {
        ex_.printStackTrace();
        // 转换失败直接返回原始JSON字符串
        return new ResponseEntity<>(msg, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

3. 修复非2xx响应的处理逻辑

原代码直接返回response对象会导致序列化问题,需解析响应体后再返回:

else if(response!=null && !response.getStatusCode().is2xxSuccessful()){
    String errorBody = new String(response.getBody(), StandardCharsets.UTF_8);
    HttpHeaders jsonHeaders = new HttpHeaders();
    jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
    return new ResponseEntity<>(errorBody, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
}

最终核心代码片段

@GetMapping(value="api/image/{id}" , produces = { MediaType.IMAGE_JPEG_VALUE, MediaType.APPLICATION_JSON_VALUE })
public @ResponseBody ResponseEntity<Object> api_image_customer(@PathVariable(name = "id")String id){
    String accessToken = getCrmAccessToken();
    RestTemplate restTemplate = new RestTemplate();

    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-Type", MediaType.APPLICATION_JSON_VALUE);
    headers.add("Authorization", "Bearer "+accessToken);
    ResponseEntity<byte[]> response = null;
    HttpEntity formEntity = new HttpEntity(null, headers);

    try {
        response = restTemplate.exchange("http://172.18.5.19:8082/api/image/"+id, HttpMethod.GET,
                formEntity, byte[].class);

        if(response!=null && response.getStatusCode().is2xxSuccessful()) {
            byte[] imageBytes = response.getBody();
            HttpHeaders imageHeaders = new HttpHeaders();
            imageHeaders.setContentType(MediaType.IMAGE_JPEG);
            return new ResponseEntity<>(imageBytes, imageHeaders, HttpStatus.OK);
        } else if(response!=null && !response.getStatusCode().is2xxSuccessful()){
            String errorBody = new String(response.getBody(), StandardCharsets.UTF_8);
            HttpHeaders jsonHeaders = new HttpHeaders();
            jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
            return new ResponseEntity<>(errorBody, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
        } else {
            HttpHeaders jsonHeaders = new HttpHeaders();
            jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
            return new ResponseEntity<>("{\"msg\":\"ERROR\"}", jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    } catch(HttpClientErrorException ex) {
        String msg = ex.getResponseBodyAsString();
        HttpHeaders jsonHeaders = new HttpHeaders();
        jsonHeaders.setContentType(MediaType.APPLICATION_JSON);
        
        try {
            ObjectMapper objectMapper = new ObjectMapper();
            Map<String, Object> errorMap = objectMapper.readValue(msg, Map.class);
            return new ResponseEntity<>(errorMap, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
        } catch (Exception ex_) {
            ex_.printStackTrace();
            return new ResponseEntity<>(msg, jsonHeaders, HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }
}

通过上述修改,即可根据响应场景动态设置正确的Content-Type,同时解决消息转换器的异常问题。

内容的提问来源于stack exchange,提问作者JPG

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 15:36:17