1. 问题描述
在写SpringBoot项目的时候,由于是前后端分离项目,为了统一接口形式,使用一个类封装了返回数据的形式,但是在测试的时候报异常HttpMessageNotWritableException
后续附上解决办法(有些时候看上去一头雾水的bug可能只是一个很小的问题 so不要放弃~!)
封装类的代码:
1 package com.xiaobai.realtimesystem.realtimeserver.utils; 2 3 /** 4 * @author xiaobai 5 * @version 1.0 6 * @date 2019/5/22 16:43 7 * @email baijinfeng1202@gmail.com 8 * @address www.rbx1213.top 9 * @describe 用于实现接口返回规范的类 所有接口返回值都由该类封装 10 */ 11 @SuppressWarnings("ALL") 12 public class AppResponse<T> { 13 /** 14 * 返回值代码 由Status类定义 15 * @see Status 16 */ 17 private int code; 18 /** 19 * 返回的附加提示信息 20 */ 21 private String msg; 22 /** 23 * 返回的数据 若没有则为空 24 */ 25 private T data; 26 27 /** 28 * 无参构造必须有 29 */ 30 private AppResponse(){ } 31 32 /** 33 * 全参构造方法 34 * @param code 状态码 35 * @param msg 附加提示信息 36 * @param data 数据 37 */ 38 private AppResponse(int code,String msg,T data){ 39 this.code = code; 40 this.msg = msg; 41 this.data = data; 42 } 43 /** 44 * 成功消息的返回 45 * +2 重载方法 46 * @return 47 */ 48 public static AppResponse success(){ 49 return builder().code(Status.OK).build(); 50 } 51 52 /** 53 * 成功消息的返回 54 * +2 重载方法 55 * @param msg 附加提示消息 56 * @return 57 */ 58 public static AppResponse success(String msg){ 59 return builder().code(Status.OK).msg(msg).build(); 60 } 61 62 /** 63 * 成功消息的返回 64 * +2 重载方法 65 * @param msg 附加提示消息 66 * @param data 需要返回的数据 67 * @param <T> 68 * @return 69 */ 70 public static <T> AppResponse success(String msg,T data){ 71 return builder().code(Status.OK).msg(msg).data(data).build(); 72 } 73 74 /** 75 * 出错消息提示 该方法要求必须返回错误消息 76 * @param msg 错误消息 77 * @return 78 */ 79 public static AppResponse error(String msg){ 80 return builder().code(Status.ERR).msg(msg).build(); 81 } 82 83 /** 84 * 产生异常提示 必须返回异常消息 85 * @param msg 异常消息 86 * @return 87 */ 88 public static AppResponse exception(String msg){ 89 return builder().code(Status.EXCEPTION).msg(msg).build(); 90 } 91 92 /** 93 * 未找到的提示消息返回 94 * @return 95 */ 96 public static AppResponse notFound(){ 97 return builder().code(Status.NOTFOND).build(); 98 } 99 100 /** 101 * 构建一个AppResponseBuilder 对象 102 * @param <T> 103 * @return 104 */ 105 public static <T> AppResponseBuilder builder(){ 106 return new AppResponseBuilder(); 107 } 108 109 /** 110 * 内部类 用来解决static 不能使用泛型的问题 111 * @param <T> 112 */ 113 private static class AppResponseBuilder<T>{ 114 /** 115 * 返回值代码 由Status类定义 116 * @see Status 117 */ 118 private int code; 119 /** 120 * 返回的附加提示信息 121 */ 122 private String msg; 123 /** 124 * 返回的数据 若没有则为空 125 */ 126 private T data; 127 128 /** 129 * 构建一个AppResponse 对象 130 * @return 131 */ 132 public AppResponse build() { 133 return new AppResponse<T>(this.code, this.msg, this.data); 134 } 135 /** 136 * 链式调用 设置状态码 137 * @param status 状态 参考Status 138 * @return 139 */ 140 public AppResponseBuilder code(Status status) { 141 this.code = status.value; 142 return this; 143 } 144 /** 145 * 链式调用 设置提示信息 146 * @param msg 提示信息 147 * @return 148 */ 149 public AppResponseBuilder msg(String msg) { 150 this.msg = msg; 151 return this; 152 } 153 /** 154 * 链式调用 设置返回数据 155 * @param data 返回数据 156 * @return 157 */ 158 public AppResponseBuilder data(T data) { 159 this.data = data; 160 return this; 161 } 162 } 163 164 @Override 165 public String toString() { 166 return "AppResponse{" + 167 "code=" + code + 168 ", msg='" + msg + '\'' + 169 ", data=" + data + 170 '}'; 171 } 172 }
postman 测试报错截图:
具体错误(为了方便大家查看是否和我是一样的错误,请ctrl +F 搜索报错关键字!):
Resolved [org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.xiaobai.realtimesystem.realtimeserver.utils.AppResponse]
{"timestamp":"2019-05-22T09:41:18.799+0000","status":500,"error":"Internal Server Error","message":"No converter found for return value of type: class com.xiaobai.realtimesystem.realtimeserver.utils.AppResponse","trace":"org.springframework.http.converter.HttpMessageNotWritableException: No converter found for return value of type: class com.xiaobai.realtimesystem.realtimeserver.utils.AppResponse\r\n\tat org.springframework.web.servlet.mvc.method.annotation.AbstractMessageConverterMethodProcessor.writeWithMessageConverters(AbstractMessageConverterMethodProcessor.java:233)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor.handleReturnValue(RequestResponseBodyMethodProcessor.java:180)\r\n\tat org.springframework.web.method.support.HandlerMethodReturnValueHandlerComposite.handleReturnValue(HandlerMethodReturnValueHandlerComposite.java:82)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:122)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:892)\r\n\tat org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:797)\r\n\tat org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:87)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1039)\r\n\tat org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)\r\n\tat org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)\r\n\tat org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:908)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:660)\r\n\tat org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)\r\n\tat javax.servlet.http.HttpServlet.service(HttpServlet.java:741)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:53)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:92)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)\r\n\tat org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)\r\n\tat org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)\r\n\tat org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:200)\r\n\tat org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)\r\n\tat org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:490)\r\n\tat org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:139)\r\n\tat org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:92)\r\n\tat org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74)\r\n\tat org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:343)\r\n\tat org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:408)\r\n\tat org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)\r\n\tat org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:836)\r\n\tat org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1747)\r\n\tat org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)\r\n\tat java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)\r\n\tat java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)\r\n\tat org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)\r\n\tat java.lang.Thread.run(Thread.java:748)\r\n","path":"/account/register"}
2. 造成原因:
我的原因是没有为属性生成get/set方法(对,就是这么简单!)导致无法获取属性的值进而导致转换成json报错
网上还有说其他原因的,比如springboot自动配置的json转换器不好用,或者是因为对象中younull值导致转换失败等,具体原因请大家自行分析。
3.解决办法:
解决办法就很简单了,给所有的属性加上get/set方法即可解决问题!
附上我解决问题后的代码:(代码中标红的部分!)
1 package com.xiaobai.realtimesystem.realtimeserver.utils; 2 3 /** 4 * @author xiaobai 5 * @version 1.0 6 * @date 2019/5/22 16:43 7 * @email baijinfeng1202@gmail.com 8 * @address www.rbx1213.top 9 * @describe 用于实现接口返回规范的类 所有接口返回值都由该类封装 10 */ 11 @SuppressWarnings("ALL") 12 public class AppResponse<T> { 13 /** 14 * 返回值代码 由Status类定义 15 * @see Status 16 */ 17 private int code; 18 /** 19 * 返回的附加提示信息 20 */ 21 private String msg; 22 /** 23 * 返回的数据 若没有则为空 24 */ 25 private T data; 26 27 /** 28 * 无参构造必须有 29 */ 30 private AppResponse(){ } 31 32 /** 33 * 全参构造方法 34 * @param code 状态码 35 * @param msg 附加提示信息 36 * @param data 数据 37 */ 38 private AppResponse(int code,String msg,T data){ 39 this.code = code; 40 this.msg = msg; 41 this.data = data; 42 } 43 //=============================start of get/set 集合======================== 44 45 public int getCode() { 46 return code; 47 } 48 49 public void setCode(int code) { 50 this.code = code; 51 } 52 53 public String getMsg() { 54 return msg; 55 } 56 57 public void setMsg(String msg) { 58 this.msg = msg; 59 } 60 61 public T getData() { 62 return data; 63 } 64 65 public void setData(T data) { 66 this.data = data; 67 } 68 //=============================end of get/set 集合======================== 69 70 /** 71 * 成功消息的返回 72 * +2 重载方法 73 * @return 74 */ 75 public static AppResponse success(){ 76 return builder().code(Status.OK).build(); 77 } 78 79 /** 80 * 成功消息的返回 81 * +2 重载方法 82 * @param msg 附加提示消息 83 * @return 84 */ 85 public static AppResponse success(String msg){ 86 return builder().code(Status.OK).msg(msg).build(); 87 } 88 89 /** 90 * 成功消息的返回 91 * +2 重载方法 92 * @param msg 附加提示消息 93 * @param data 需要返回的数据 94 * @param <T> 95 * @return 96 */ 97 public static <T> AppResponse success(String msg,T data){ 98 return builder().code(Status.OK).msg(msg).data(data).build(); 99 } 100 101 /** 102 * 出错消息提示 该方法要求必须返回错误消息 103 * @param msg 错误消息 104 * @return 105 */ 106 public static AppResponse error(String msg){ 107 return builder().code(Status.ERR).msg(msg).build(); 108 } 109 110 /** 111 * 产生异常提示 必须返回异常消息 112 * @param msg 异常消息 113 * @return 114 */ 115 public static AppResponse exception(String msg){ 116 return builder().code(Status.EXCEPTION).msg(msg).build(); 117 } 118 119 /** 120 * 未找到的提示消息返回 121 * @return 122 */ 123 public static AppResponse notFound(){ 124 return builder().code(Status.NOTFOND).build(); 125 } 126 127 /** 128 * 构建一个AppResponseBuilder 对象 129 * @param <T> 130 * @return 131 */ 132 public static <T> AppResponseBuilder builder(){ 133 return new AppResponseBuilder(); 134 } 135 136 /** 137 * 内部类 用来解决static 不能使用泛型的问题 138 * @param <T> 139 */ 140 private static class AppResponseBuilder<T>{ 141 /** 142 * 返回值代码 由Status类定义 143 * @see Status 144 */ 145 private int code; 146 /** 147 * 返回的附加提示信息 148 */ 149 private String msg; 150 /** 151 * 返回的数据 若没有则为空 152 */ 153 private T data; 154 155 /** 156 * 构建一个AppResponse 对象 157 * @return 158 */ 159 public AppResponse build() { 160 return new AppResponse<T>(this.code, this.msg, this.data); 161 } 162 /** 163 * 链式调用 设置状态码 164 * @param status 状态 参考Status 165 * @return 166 */ 167 public AppResponseBuilder code(Status status) { 168 this.code = status.value; 169 return this; 170 } 171 /** 172 * 链式调用 设置提示信息 173 * @param msg 提示信息 174 * @return 175 */ 176 public AppResponseBuilder msg(String msg) { 177 this.msg = msg; 178 return this; 179 } 180 /** 181 * 链式调用 设置返回数据 182 * @param data 返回数据 183 * @return 184 */ 185 public AppResponseBuilder data(T data) { 186 this.data = data; 187 return this; 188 } 189 } 190 191 @Override 192 public String toString() { 193 return "AppResponse{" + 194 "code=" + code + 195 ", msg='" + msg + '\'' + 196 ", data=" + data + 197 '}'; 198 } 199 }
再次测试:
问题解决(哈哈,方法还没有实现,但是接口可以正常返回数据就说明bug已经解决了)