问题描述

  最近在开发时候碰到一个问题,springmvc页面向后台传数据的时候,通常我是这样处理的,在前台把数据打成一个json,在后台接口中使用@requestbody定义一个对象来接收,但是这次数据传不过去,报400的错误,原因也很容易想到,该对象有一个属性也是一个对象,属性对象是用抽象类定义的,他有几个具体实现,具体实现中的字段都是不一样的,springmvc是不会自动识别并注入你使用的是哪一个实现类的.所以无法传过来.

传递对象如下:

@Data
public class ActivityRule {...private RuleDetail ruleDetail;//注意这里的RuleDetail是一个抽象类...
}

  解决方案:

  使用自定义消息转换器,首先让我们来了解一下spring的消息转换器

springmvc的消息转换器(HTTPMessageConverter)

  我们都使用过@RequestBody和@ResponseBody这两个注解,他们的作用就是在前台向后台传递数据时,把请求报文中的数据通过springmvc的处理成一个我们自己定义的对象,在这个过程中首先springmvc会去请求头中找到一个contentType的属性,然后去匹配能够处理这种类型的消息转换器,而在返回数据时,再把对象转换成响应报文.

介绍一下contentType属性:

  contentType是requestHeader中的一个属性,这个头部主要用于说明body中的字符串是什么格式的,比如:text,json,xml,html等。springmvc解析请求时,首先通过此头部,才能确定使用什么格式来解析请求体中的字符串,对于响应报文,浏览器也是要通过这个属性,来确定在如何处理响应报文的返回数据。
介绍一下@RequestBody/@ResponseBody注解
  当用该注解标注一个对象时,在请求过程中进行数据映射时,spring会根据Request对象header部分的content-Type类型,逐一匹配合适的HttpMessageConverter来读取数据,而在响应时,spring会根据Request对象header部分的Accept属性(逗号分隔),逐一按accept中的类型,去遍历找到能处理的HttpMessageConverter.

  到这里我们就有了一种思路,能不能让我们来接管请求报文到对象映射这个过程,只要我们得到了json字段,根据内容我们就知道需要去映射哪个类,至此,我们有了思路就可以去实现了,我们可以通过spring的消息转换器来实现我们的想法,

  默认情况下,spring使用HttpMessageConverter来负责将请求信息转换为一个对象(类型为 T),并且将对象(类型为 T)输出为响应信息,如果自定义我们自己的消息转换器,则需要新建一个类,继承

AbstractHttpMessageConverter<T>,下面看我针对上面的ActivityRule对象定义的一个消息转换器.

/*** 自定义消息转换器 ActivityRule* @author yogo.wang* @date 2017/10/25-下午5:43.*/
public class RuleMessageConverter extends AbstractHttpMessageConverter<ActivityRule> {public RuleMessageConverter(){super(new MediaType("application","x-rule", Charset.forName("UTF-8")));}@Overrideprotected boolean supports(Class<?> clazz) {return ActivityRule.class.isAssignableFrom(clazz);}@Overrideprotected ActivityRule readInternal(Class<? extends ActivityRule> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {String temp= StreamUtils.copyToString(inputMessage.getBody(),Charset.forName("UTF-8"));Map<String,Object> map = (Map<String,Object>)JSON.parse(temp);RuleType ruleType = RuleType.valueOf((String)map.get("ruleType"));String ruleDetail = StringUtils.substringAfter(temp, "ruleDetail\":");ruleDetail=ruleDetail.substring(0,ruleDetail.length()-1);ActivityRule rule=new ActivityRule();rule.setName((String)map.get("name"));rule.setRuleType(ruleType);switch (ruleType){case LOGIN:rule.setRuleDetail(JSON.parseObject(ruleDetail, LoginRuleDetail.class));break;case ROLE_UPGRADE:rule.setRuleDetail(JSON.parseObject(ruleDetail, UpgradeRuleDetail.class));break;case PAY_AMOUNT:rule.setRuleDetail(JSON.parseObject(ruleDetail, RechargeRuleDetail.class));break;default:rule.setRuleDetail(null);}return rule;}@Overrideprotected void writeInternal(ActivityRule activityRule, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {}
}

  在上面的类中,在继承抽象类AbstractHttpMessageConverter时,我们将泛型指定为@RequestBody标注的了类,即ActivityRule类,然后在该类中的构造器中,我们创建了一个新的媒体类型"x-rule",名称可以自定义,并且指定相应的编码方式,一般都是utf-8。在重写的support()方法中,我们来判断所支持的Class是否与ActivityRule的Class相同,只有相同,才会走下面的方法readInternal,在这个方法里,我们就需要从请求头里拿到json字符串,然后自己手动将json映射成对象.

  写完这个类还没完,还有两步操作是必须的,第一,在spring的配置文件将消息转换器配置上,如下:

 <mvc:annotation-driven><mvc:message-converters><bean class="com.ximalaya.cms.games.operation.activity.service.converter.RuleMessageConverter"><property name="supportedMediaTypes"><list><value>application/json</value><value>application/x-rule</value></list></property></bean><bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/></mvc:message-converters></mvc:annotation-driven>

  第二,在controller接口中,需要手动指定哪个接口可以接收我们自定义的媒体类型.如下:

 /*** 保存规则对象* @param rule* @return*/@RequestMapping(method = RequestMethod.POST,produces = { "application/x-rule"})@ResponseBodypublic ResponseMessage save(@RequestBody ActivityRule rule) {LOG.info("begin to save ActivityRule:{}",rule);try{ruleService.saveAvtivityRule(rule);return ResponseMessage.ok();}catch (Exception e){return ResponseMessage.fail(e.getMessage());}}

  以上操作完成后,在我测试的时候,踩了两个坑,需要特别说明一下.在我运行时,数据还是过不来,报415的错误,说不支持的媒体类型,后来发现在前端的Ajax调用中,发现contentType没改,改后如下:

                    $.ajax({method: $form.attr('method'),traditional: true,url: $form.attr('action'),data: JSON.stringify(rule),contentType: "application/x-rule",dataType:"json",success: function (ret) {//,......
                        },error: function (message) {alert('ERROR:' + JSON.stringify(message));}});

 再次运行,没问题,json字段如愿映射成了我们想要的对象,但在前端返回的时候,仍然有错误,报406,根据网上的解决方案,说是缺少fastjson相关包,于是引入了相关jar报,还是没解决,卡了大半天,终,修改了一下@RequestMapping()的内容,神奇的解决了问题,如下:

 /*** 保存规则对象* @param rule* @return*/@RequestMapping(method = RequestMethod.POST,produces = { "application/x-rule","application/json"})@ResponseBodypublic ResponseMessage save(@RequestBody ActivityRule rule) {LOG.info("begin to save ActivityRule:{}",rule);try{ruleService.saveAvtivityRule(rule);return ResponseMessage.ok();}catch (Exception e){return ResponseMessage.fail(e.getMessage());}}

  我猜测,原因可能是这样的,由于设置了@ResponseBody,要把对象转换成json格式,但是注意看我的代码,@ResponseBody标注的类是ResponseMessage,不是ActivityRule!!!!,而coontentType被我设成了application/x-rule,所以在返回的时候,仍然走了我自定义的那个消息转换器,而两个类肯定是不同的,support返回了false,而我添加了application/json以后,ResponseMessage对象就没有走我自定义的消息转换器,而是以json的contentType进入了spring的默认消息转换器,并且成功映射到响应体中.

转载于:https://www.cnblogs.com/fingerboy/p/7753577.html

SpringMVC自定义配置消息转换器踩坑总结相关推荐

  1. 在 springmvc.xml 中配置消息转换器处理 ResponseBody 中文乱码

    在 springmvc.xml 中配置消息转换器处理 ResponseBody 中文乱码 <mvc:annotation-driven><!-- 消息转换器 --><mv ...

  2. (配置消息转换器) sso单点登入之jsonp改进版

    原: 后台controller中 需要 返回 [ callback(json数据)] 改进的做法: 改进后所有 后台controller中的接口返回参数不用做任何改变: 之前的格式是需要 返回 [ c ...

  3. (配置消息转换器)解决后台返回json数据到前台时页面时中文显示乱码问题

    (配置消息转换器)解决后台返回json数据到前台时页面时中文显示乱码问题 SpringMVC.xml中加 <!-- 配置消息转换器(解决中文乱码问题)--><mvc:annotati ...

  4. iOS自动化测试(一)-技术方案、环境配置与疯狂踩坑

    移动端的自动化测试,最常见的就是Android自动化测试,IOS相较于Android要安全稳定的多,但是IOS也是一个需要测试的方向,作为测试,不认真测一下心里还是过意不去,只是我个人觉得Androi ...

  5. k8s containerd集群配置安装完整踩坑教程

    完整踩坑和精简内容 k8s containerd配置 containerd安装参考 k8s安装参考 环境 两台机器 hostnamectl set-hostname master hostnamect ...

  6. Apollo 配置教程以及踩坑总结

    一.Apollo包的介绍 apollo一共三个包:apollo-portal.zip.apollo-configservice.zip.apollo-adminservice.zip 说明: port ...

  7. SprinBoot实现接管SpringMVC自定义配置

    只需要添加一个类型为WebMvcConfigurer的类,并使用注解@Configuration标注即可重写SpringMVC的配置,如果想要全面接管,即只有写过的配置生效,不使用默认配置,再添加注解 ...

  8. react-antd table树形数据默认展开行实现以及自定义图标实现及踩坑(defaultExpandedRowKeys,expandedRowKeys)

    前言: 最近在做一个权限管理的后台项目,经常涉及到树形数据展示,就用到了antd,table表格树形数据组件.记录一下使用过程的坑. 环境及配置: antd -3.23.6(大版本为antd 3)   ...

  9. Alphapose - Windows下Alphapose(Pytorch 1.1+)版本2021最新环境配置步骤以及踩坑说明

    文章目录 1 Alphapose 2 环境配置 2.1 官方安装说明 2.2 Anaconda创建虚拟环境Alphapose 2.3 获取Alphapose仓库 2.4 安装相关依赖库 2.4.1 激 ...

最新文章

  1. 一种巧妙的drawable.xml替代方案
  2. linux-tq2440
  3. Linux基础命令---fold
  4. Java枚举益智游戏
  5. 8086简单的指令流水线_在8086微处理器中执行流水线的指令和概念的步骤
  6. spring boot + vue 前后端分离时间戳转换为 yyyy:MM:dd HH:mm:ss格式
  7. 实惠星扫地机器人不能开机_扫地机器人不能承受的重量,14kg法斗坐在上面,它旋转后死机...
  8. paip.索引优化---sql distict—order by 法
  9. 用什么软件写html语言,写html代码用什么软件
  10. oj系统 的c语言答案,宁波大学OJ系统前105道C语言题目及答案精讲
  11. 利用Epplus将数据导出到Excel
  12. android intent传文件夹,Android之intent传值篇
  13. linux之OPERATION(运维)一
  14. 微信环境中扫描二维码下载APP(APK)文件的解决方案-自动跳转到手机默认浏览器(AppStore)打开
  15. 哪款护眼灯最适合学生?教育部入围护眼照明品牌
  16. 英酷词典 使用方法 必知必会
  17. 分布式微服务下的跨库查询解决思路
  18. matlab画最小二乘线,matlab非线性最小二乘法求解 - 计算模拟 - 小木虫 - 学术 科研 互动社区...
  19. RSD 教程 —— §4.2 保存框架
  20. GMS 发短信的515错误

热门文章

  1. php mysql追踪器_zf框架的数据库追踪器使用示例
  2. 计算机硬件课题,计算机硬件技术基础免试课题.pptx
  3. 2021辽宁省大学生程序设计竞赛题解
  4. 牛客编程巅峰赛S2第6场 - 钻石王者
  5. redis实现可重入锁
  6. HttpRunnerManager接口自动化测试框架在win环境下搭建教程
  7. beetle.express一通讯案例测试结果
  8. Excel从右向左查找
  9. 实训汇编语言设计——内存多字节10进制数相加
  10. 25个创新的仪表板(管理面板)设计示例