解决 Could not write request: no suitable HttpMessageConverter found for request type [java.lang.Long]

项目场景:

业务服务通过RestTemplate调用文件上传服务。(

<java.version>1.8</java.version>
<spring.cloud.version>Hoxton.SR12</spring.cloud.version>
<spring.cloud.alibaba.version>2.2.9.RELEASE</spring.cloud.alibaba.version>
<spring.boot.version>2.3.12.RELEASE</spring.boot.version>


问题描述

由于restTemplate中引入了FormHttpMessageConverter消息转换器,在调用过程中,无法解析java.lang.Long类型,报错:org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [java.lang.Long]

Controller:

/**
     * @description: 文件上传
     * @param file
     * @return
     * @throws
     * @author menshaojing
     * @date 2021/11/22 10:21
     */
    @PostMapping("/upload")
    @ApiOperation(value = "文件上传")
    public ResultVo upload(@RequestBody MultipartFile file, HttpServletRequest request) throws BaseException {
        return ResultUtils.success(supplementaryZuoyeService.upload(file, request));
    }
 

service:

 /**
     * @description: 文件上传
     * @param file
     * @return
     * @throws
     * @author menshaojing
     * @date 2021/11/18 15:51
     */
    public StorePathVo upload(MultipartFile file, HttpServletRequest request) {
        String filename = FileUtils.getFilename(file);
        String suffix = filename.substring(filename.lastIndexOf('.') + 1);
        //校验文件格式
        if (!"pdf".equalsIgnoreCase(suffix) && !"docx".equalsIgnoreCase(suffix) && !"doc".equalsIgnoreCase(suffix)) {
            throw new BaseException(201, "文件格式错误");
        }
        filename = filename.substring(0, filename.lastIndexOf('.')) + ".pdf";

        FileUploadRequest fileUploadRequest = new FileUploadRequest();
        fileUploadRequest.setObjtype(FastfileTypeEnum.ACTION_FILE.getCode());
        fileUploadRequest.setFilename(file.getOriginalFilename());
        fileUploadRequest.setMimeType(file.getContentType());
        fileUploadRequest.setFilesize(file.getSize());
        fileUploadRequest.setToken(request.getParameter("token"));

        StorePathVo storeVo = null;
        try {
            storeVo = fileServerUtils.fileUpload(fileUploadRequest, file);
        } catch (IOException e) {
            throw new BaseException(ResultEnum.UPLOAD_FILE_FAIL);
        }
        if (null == storeVo) {
            throw new BaseException(ResultEnum.UPLOAD_FILE_FAIL);
        }
      ''''''''''''''
        }
    }

FileServerUtils类:

    /**
     * @param fileUploadRequest
     * @throws BaseException
     * @Description: 跨服务调用文件服务器上传文件
     * @author menshaojing
     * @date 13:22 2020/7/9
     */
    private StorePathVo upload(FileUploadRequest fileUploadRequest, Resource resource) throws BaseException {
        MultiValueMap<String, Object> paramMap = new LinkedMultiValueMap<>(1);
         paramMap.add("filesize", fileUploadRequest.getFilesize());
        paramMap.add("filesize", fileUploadRequest.getFilesize());
        paramMap.add("gradeid", fileUploadRequest.getGradeid());
        paramMap.add("subjectid", fileUploadRequest.getSubjectid());
        paramMap.add("duration", fileUploadRequest.getDuration());
        paramMap.add("mimeType", fileUploadRequest.getMimeType());
        paramMap.add("nodeid", fileUploadRequest.getNodeid());
        paramMap.add("objtype", fileUploadRequest.getObjtype());
        paramMap.add("objid", fileUploadRequest.getObjid());
        paramMap.add("filename", fileUploadRequest.getFilename());
        paramMap.add("addflag", fileUploadRequest.getAddflag());
        paramMap.add("file", resource);

        HttpHeaders headers = new HttpHeaders();
        MediaType type = MediaType.parseMediaType("multipart/form-data; charset=UTF-8");
        headers.setContentType(type);
        HttpEntity<MultiValueMap<String, Object>> formData = new HttpEntity<>(paramMap, headers);

        Integer uploadflag = fileUploadRequest.getUploadflag();
        String uploadpath = defaultpath;
        if (uploadflag == 1) {
            uploadpath = fastpath;
        } else if (uploadflag == 2) {
            uploadpath = osspath;
        }
        String url =buildURL(RestConstant.fileService + uploadpath, null == fileUploadRequest.getToken() ? "" : fileUploadRequest.getToken());
        log.info("跨服务调用文件服务器上传>>>:url={}", url);
        ResultVo result = restTemplate.postForObject(url, formData, ResultVo.class);
        if (result.getCode() != 200) {
            throw new BaseException(205, "跨服务调用文件服务器上传文件有误!");
        }
        log.info("上传成功!!!");
        return JSONObject.parseObject(JSONObject.toJSONString(result.getData()), StorePathVo.class);
    }
 public static String buildURL(String orignalURL, String token) {

        if (!orignalURL.contains("?")) {
            return orignalURL + "?token=" + token;
        } else {
            return orignalURL + "&token=" + token;
        }

    }

FileUploadRequest 类:


import lombok.Data;

/**
 * @author menshaojing
 * @Description: 文件上传服务器参数封装
 * @date 13:33 2020/7/9
 */
@Data
public class FileUploadRequest {

    /**
     * 课本ID
     */
    private Integer bookid;
    /**
     * 时长
     */
    private Integer duration;
    /**
     * 年级ID
     */
    private Integer gradeid;
    /**
     * 章节ID
     */
    private Integer nodeid;
    /**
     * 类型
     */
    private Byte objtype;
    /**
     * 某资源ID,例如导学ID,作业ID,资源库ID等
     */
    @JsonFormat(shape = JsonFormat.Shape.STRING)
    private Long objid;
    /**
     * 科目ID
     */
    private Integer subjectid;
    /**
     * 文件类型
     */
    private String mimeType;
    /**
     * 文件大小
     */
    private Long filesize;
    /**
     * token
     */
    private String token;
    /**
     * 文件名
     */
    private String filename;
    /**
     * 不传默认新建fastfile记录,传0表示不操作fastfile表
     */
    private Integer addflag;
    /**
     * 上传方式 0 默认上传 1 fast上传 2 oss上传
     */
    private Integer uploadflag = 0;
}

在这里插入图片描述
上传服务实现的方法:

    public StorePathVo uploadFile(HttpServletRequest request, AbstractUpload abstractUpload) throws Exception {
        UserRelationInfo userInfo = (UserRelationInfo) request.getAttribute("user");
        StorePath storePath = null;
        String fileName = "";
        StorePathVo storePathVo = new StorePathVo();
        UploadFileRequest uploadFileRequest = new UploadFileRequest();
        String ext = "";
        try {

            StandardMultipartHttpServletRequest standardMultipartHttpServletRequest=
                    new  StandardMultipartHttpServletRequest(request);
            final Map<String, String[]> parameterMap = standardMultipartHttpServletRequest.getParameterMap();

            final MultiValueMap<String, MultipartFile> multiFileMap = standardMultipartHttpServletRequest.getMultiFileMap();

            for (Map.Entry<String,String[]> entry:parameterMap.entrySet()) {

                    String itemKey = entry.getKey();
                    String itemVal = entry.getValue()[0];
                    switch (itemKey) {
                        case "bookid":
                            uploadFileRequest.setBookid(Integer.parseInt(itemVal));
                            break;
                        case "nodeid":
                            uploadFileRequest.setNodeid(Integer.parseInt(itemVal));
                            break;
                        case "gradeid":
                            uploadFileRequest.setGradeid(Integer.parseInt(itemVal));
                            break;
                        case "subjectid":
                            uploadFileRequest.setSubjectid(Integer.parseInt(itemVal));
                            break;
                        case "duration":
                            uploadFileRequest.setDuration(Integer.parseInt(itemVal));
                            break;
                        case "objid":
                            uploadFileRequest.setObjid(Long.parseLong(itemVal));
                            break;
                        case "objtype":
                            uploadFileRequest.setObjtype(Byte.parseByte(itemVal));
                            break;
                        case "filesize":
                            uploadFileRequest.setFilesize(Integer.parseInt(itemVal));
                            break;
                        case "mimeType":
                            uploadFileRequest.setMimeType(itemVal);
                            break;
                        case "addflag":
                            uploadFileRequest.setAddflag(Byte.parseByte(itemVal));
                            break;
                        case "uuid":
                            uploadFileRequest.setUuid(itemVal);
                            if (!org.apache.commons.lang.StringUtils.isBlank(itemVal) &&
                                    redisTemplate.opsForValue().get(itemVal) == null) {
                                throw new BaseException(ResultEnum.NO_AUTHORITY_TO_OPERATE_RESOURCE);
                            }
                            break;
                        default:
                            break;
                    }

            }
            final Set<String> fileNames = multiFileMap.keySet();

            for (String file:fileNames) {
                if (uploadFileRequest.getFilesize() <= 0) {
                    throw new BaseException(ResultEnum.UPLOAD_FILE_ISEMPTY);
                }
                checkUploadFileSize(uploadFileRequest, request);
                final List<MultipartFile> multipartFiles = multiFileMap.get(file);
                for (MultipartFile multipartFile: multipartFiles) {
                    final InputStream inputStream = multipartFile.getInputStream();
                    if (inputStream != null) {
                        fileName =URLDecoder.decode(multipartFile.getOriginalFilename(), "UTF-8");;
                        ext = fileName.substring(fileName.lastIndexOf(".") + 1, fileName.length()).toLowerCase();

                        if (uploadFileRequest.getMimeType() == null) {
                            uploadFileRequest.setMimeType(multipartFile.getContentType());
                        }
                        if (null != abstractUpload) {
                            storePath = abstractUpload.uploadFile(inputStream, uploadFileRequest.getFilesize(), ext);
                        } else {
                            storePath = uploadAdapter.uploadFile(inputStream, uploadFileRequest.getFilesize(), ext);
                        }
                        inputStream.close();
                    }
                }

            }


        } catch (FileUploadException e) {
            throw (FileUploadException) e.getCause();
        } catch (IOException e) {
            throw new FileUploadIOException(new FileUploadException(String.format("Processing of %s request failed. %s",
                    ServletFileUpload.MULTIPART_FORM_DATA, e.getMessage()), e));
        } catch (Exception e) {
            log.error("上传文件失败,error:{}", e);
            throw new BaseException(ResultEnum.UPLOAD_FILE_FAIL);
        }

        if (null == storePath) {
            log.error("上传文件失败,storePath=null,uploadFileRequest:{}", uploadFileRequest);
            throw new BaseException(ResultEnum.UPLOAD_FILE_FAIL);
        }
        storePathVo.setFullPath(storePath.getFullPath());
        storePathVo.setPath(storePath.getPath());

        storePathVo.setHasDomainPath(fastDfsUploadUtils.getFastfileWebPath(storePath.getFullPath()));


        storePathVo.setCreatedAt(new Date());
        storePathVo.setGroup(storePath.getGroup());
        storePathVo.setFname(fileName);

        if (uploadFileRequest.getAddflag().equals((byte) 0)) {
            return storePathVo;
        }

        //写入数据库
        String tableName = "";
        Long objid = 0L;
        if (StringUtils.isNullOrBlank(uploadFileRequest.getUuid())) {
            tableName = tableUtil.getTableName(request, SplitTableNameEnum.FASTFILE.getSchema(), SplitTableNameEnum.FASTFILE.getName());
            objid = uploadFileRequest.getObjid();
        } else {
            tableName = tableUtil.getUUIDTableName(uploadFileRequest.getUuid());
            objid = (long) uploadFileRequest.getUuid().hashCode();
        }

        Fastfile fastfile = new Fastfile();
        fastfile.setFfid(IdWorker.getId());
        fastfile.setSchoolid(userInfo.getSchoolid());
        fastfile.setCampusid(userInfo.getCampusid());
        fastfile.setGradeid(uploadFileRequest.getGradeid());
        fastfile.setTermid(userInfo.getTermid());
        fastfile.setSubjectid(uploadFileRequest.getSubjectid());
        fastfile.setDuration(uploadFileRequest.getDuration());
        fastfile.setBookid(uploadFileRequest.getBookid());
        fastfile.setNodeid(uploadFileRequest.getNodeid());
        fastfile.setObjid(objid);
        fastfile.setObjtype(uploadFileRequest.getObjtype());
        fastfile.setCreateby(userInfo.getUid());
        fastfile.setFilesize(uploadFileRequest.getFilesize() / 1024);
        fastfile.setMimetype(uploadFileRequest.getMimeType());
        fastfile.setFname(fileName);
        fastfile.setRemark("");
//        if("cos".equals(storageType)) {
//            fastfile.setFullpath(ossUtils.converUrl(storePath.getFullPath(), fileName));
//        } else {
        fastfile.setFullpath(storePath.getFullPath());
//        }
        fastfile.setFgroup(storePath.getGroup());
        fastfile.setPath(storePath.getPath());
        fastfile.setCreatedAt(new Date());
        fastfile.setUpdatedAt(new Date());
        fastfile.setStatus((byte) 1);
        fastfile.setOriginalvideo((byte) 1);
        //fastfile.setCanplay((byte) 1);
        fastfileMapper.insertByTablename(fastfile, tableName);
        storePathVo.setFfid(fastfile.getFfid());
        return storePathVo;
    }


原因分析:

我们远程调用时设置的MediaType type = MediaType.parseMediaType(“multipart/form-data; charset=UTF-8”);,当restTemplate.postForObject(url, formData, ResultVo.class)调用时,代码跟踪至org.springframework.web.client.RestTemplate类的934行处,这里我们看到有调用相关的数据包装转换类,我们看构造函数中,注册好多转换类,这一块就会调用multipart/form-data类型的消息转换器,也就是FormHttpMessageConverter
在这里插入图片描述
这是我初始化的restTemplate,添加了FormHttpMessageConverter 。

  @Bean
    @LoadBalanced
    RestTemplate restTemplate(RestHeaderInterceptor restHeaderInterceptor) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
         List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        List<HttpMessageConverter<?>> updateMessageConverters=new ArrayList<>();
        // restTemplate对中文格式数据进行处理 解决中文乱码 menshaojing 2023年3月21日13:16:09
        FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
        partConverters.add(new ObjectToStringHttpMessageConverter(new DefaultConversionService(),StandardCharsets.UTF_8));
        formHttpMessageConverter.setPartConverters(partConverters);
        updateMessageConverters.add(formHttpMessageConverter);
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if (messageConverter instanceof StringHttpMessageConverter) {
                 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
                updateMessageConverters.add(stringHttpMessageConverter);
            }else {
                updateMessageConverters.add(messageConverter);
            }
        }
        restTemplate.setMessageConverters(updateMessageConverters);
        restTemplate.setInterceptors(Collections.singletonList(new RestTemplateTraceIdInterceptor()));
        restTemplate.setInterceptors(Collections.singletonList(restHeaderInterceptor));
        return restTemplate;
    }

然而FormHttpMessageConverter 中,对于数据字段转换仅支持byte[]、string、Resource的数据转换,没有我们常见的其他类型。
在这里插入图片描述
在进行源码分析时,发现有个类ObjectToStringHttpMessageConverter,这意思很明显就是将其他类型转换成string类型。我们再看它的构造函数,有一个必须参数ConversionService类(转换服务),

在这里插入图片描述
我们点进去看一下都有哪些实现类,发现有一个以Default开头的类DefaultConversionService
在这里插入图片描述
再次去查看DefaultConversionService的构造函数,发现它内置好多的数据转换类,这说明可以使用该类,进行各个类型之间的转换。
在这里插入图片描述
在这里插入图片描述

解决方案:

从原因分析中我们可以得知,只要在FormHttpMessageConverter中添加ObjectToStringHttpMessageConverter,ObjectToStringHttpMessageConverter中引入DefaultConversionService就可以实现其他类型数据转换成string类型。

解决方法1:

   @Bean
    @LoadBalanced
    RestTemplate restTemplate(RestHeaderInterceptor restHeaderInterceptor) {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
         List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        List<HttpMessageConverter<?>> updateMessageConverters=new ArrayList<>();
        // restTemplate对中文格式数据进行处理 解决中文乱码 menshaojing 2023年3月21日13:16:09

        FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
        List<HttpMessageConverter<?>> partConverters = new ArrayList<>();
        partConverters.add(new ByteArrayHttpMessageConverter());
        partConverters.add(new StringHttpMessageConverter());
        partConverters.add(new ResourceHttpMessageConverter());
        // 解决org.springframework.http.converter.HttpMessageNotWritableException: Could not write request: no suitable HttpMessageConverter found for request type [java.lang.Long]...
        //将所有类型转换成string类型
        // menshaojing 2023年4月12日15:18:58
        partConverters.add(new ObjectToStringHttpMessageConverter(new DefaultConversionService(),StandardCharsets.UTF_8));
        formHttpMessageConverter.setPartConverters(partConverters);
        updateMessageConverters.add(formHttpMessageConverter);
        for (HttpMessageConverter<?> messageConverter : messageConverters) {
            if (messageConverter instanceof StringHttpMessageConverter) {
                 StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
                updateMessageConverters.add(stringHttpMessageConverter);
            }else {
                updateMessageConverters.add(messageConverter);
            }
        }
        restTemplate.setMessageConverters(updateMessageConverters);
        restTemplate.setInterceptors(Collections.singletonList(new RestTemplateTraceIdInterceptor()));
        restTemplate.setInterceptors(Collections.singletonList(restHeaderInterceptor));
        return restTemplate;
    }

解决方法2:
我直接从restTemplate初始化配置中删除FormHttpMessageConverter ,发现数据可以正常转换了,说明了restTemplate中其他内置的消息转换器也能对multipart/form-data类型的数据进行转换。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值