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已經解決了)