場景:在微服務(wù)中,一般返回數(shù)據(jù)都會有個返回碼、返回信息和返回消息體,但是每次返回時候調(diào)用或者是封裝,太過麻煩,有沒有什么辦法不用每次都封裝呢?
答案是有的。
返回值對象 ResponseData
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
package com.study.auth.comm; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.serializer.SerializerFeature; import java.io.Serializable; /** * @Package: com.study.auth.comm * @Description: <返回數(shù)據(jù)> * @Author: MILLA * @CreateDate: 2018/4/8 9:10 * @UpdateUser: MILLA * @UpdateDate: 2018/4/8 9:10 * @Version: 1.0 */ public final class ResponseData<T> implements Serializable { private static final long serialVersionUID = 7824278330465676943L; private static final String SUCCESS_CODE = "1000" ; private static final String SUCCESS_MSG = "success" ; /** * 響應(yīng)編碼 */ @JSONField (serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 1 ) private String code; /** * 響應(yīng)提示 */ @JSONField (serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 2 ) private String msg; /** * 返回的數(shù)據(jù) */ @JSONField (serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 10 ) private T data; public static ResponseData success() { return initData(SUCCESS_CODE, SUCCESS_MSG, null ); } public static ResponseData error(String code) { String msg = PropertiesReaderUtil.getProperty(code, null ); return initData(code, msg, null ); } public static ResponseData error(String code, String msg) { return initData(code, msg, null ); } public static <T> ResponseData success(T t) { return initData(SUCCESS_CODE, SUCCESS_MSG, t); } public static <T> ResponseData errorData(String code, T data) { String msg = PropertiesReaderUtil.getProperty(code, null ); return initData(code, msg, data); } public static <T> ResponseData errorData(String code, String msg, T data) { return initData(code, msg, data); } private static <T> ResponseData initData(String code, String msg, T t) { ResponseData data = new ResponseData(SUCCESS_CODE); if (!isBlank(msg)) { data.setMsg(msg); } if (!isBlank(code)) { data.setCode(code); } if (t != null ) { data.setData(t); } return data; } private static boolean isBlank(CharSequence cs) { int strLen; if (cs != null && (strLen = cs.length()) != 0 ) { for ( int i = 0 ; i < strLen; ++i) { if (!Character.isWhitespace(cs.charAt(i))) { return false ; } } return true ; } else { return true ; } } public ResponseData() { } public ResponseData(String code) { this .code = code; } public String getCode() { return code; } public void setCode(String code) { this .code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this .msg = msg; } public T getData() { return data; } public void setData(T data) { this .data = data; } @Override public String toString() { return JSON.toJSONString( this ); } } |
如上圖的包裝,還是太繁瑣了。
裝飾者模式使用-增強類InitializingAdviceDecorator通過實現(xiàn)InitializingBean和裝飾者模式對Controller層的返回值進行包裝,大致思路:
通過RequestMappingHandlerAdapter獲取所有的返回值處理對象HandlerMethodReturnValueHandler創(chuàng)建一個新的集合存儲上一步獲取的集合(因為上一步的結(jié)果是unmodifiableList類型的)遍歷該集合找到HandlerMethodReturnValueHandler對象,將這個位置的handler替換程自定義的handler將新獲到的集合重新設(shè)置到RequestMappingHandlerAdapter的setReturnValueHandlers方法中
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
package com.study.auth.config; import com.study.auth.comm.ResponseData; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.ArrayList; import java.util.List; /** * @Package: com.study.auth.config.core * @Description: <增強controller層返回值> * @Author: milla * @CreateDate: 2020/09/04 14:42 * @UpdateUser: milla * @UpdateDate: 2020/09/04 14:42 * @UpdateRemark: <> * @Version: 1.0 */ @Configuration public class InitializingAdviceDecorator implements InitializingBean { @Autowired private RequestMappingHandlerAdapter adapter; @Override public void afterPropertiesSet() { //獲取所有的handler對象 List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers(); //因為上面返回的是unmodifiableList,所以需要新建list處理 List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers); this .decorateHandlers(handlers); //將增強的返回值回寫回去 adapter.setReturnValueHandlers(handlers); } /** * 使用自定義的返回值控制類 * * @param handlers */ private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) { for (HandlerMethodReturnValueHandler handler : handlers) { if (handler instanceof RequestResponseBodyMethodProcessor) { //找到返回值的handler并將起包裝成自定義的handler ControllerReturnValueHandler decorator = new ControllerReturnValueHandler((RequestResponseBodyMethodProcessor) handler); int index = handlers.indexOf(handler); handlers.set(index, decorator); break ; } } } /** * 自定義返回值的Handler * 采用裝飾者模式 */ private class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler { //持有一個被裝飾者對象 private HandlerMethodReturnValueHandler handler; ControllerReturnValueHandler(RequestResponseBodyMethodProcessor handler) { this .handler = handler; } @Override public boolean supportsReturnType(MethodParameter returnType) { return true ; } /** * 增強被裝飾者的功能 * * @param returnValue 返回值 * @param returnType 返回類型 * @param mavContainer view * @param webRequest 請求對象 * @throws Exception 拋出異常 */ @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { //如果已經(jīng)封裝了結(jié)構(gòu)體就直接放行 if (returnValue instanceof ResponseData) { handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); return ; } //正常返回success ResponseData success = ResponseData.success(returnValue); handler.handleReturnValue(success, returnType, mavContainer, webRequest); } } } |
配置文件讀取類PropertiesReaderUtil
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
package com.study.auth.comm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Properties; /** * @Package: com.milla.navicat.comm * @Description: <讀取配置properties工具類> * @Author: MILLA * @CreateDate: 2018/8/10 10:30 * @UpdateUser: MILLA * @UpdateDate: 2018/8/10 10:30 * @UpdateRemark: <> * @Version: 1.0 */ public final class PropertiesReaderUtil { private static final String ENCODING = "UTF-8" ; private static final Logger logger = LoggerFactory.getLogger(PropertiesReaderUtil. class ); private static Properties propsZH; private static Properties propsCN; private static String name = null ; static { //加載英文 //loadProps(false); //加載中文 loadProps( true ); } /** * 第一種,通過類加載器進行獲取properties文件流 * 第二種,通過類進行獲取properties文件流 * in = PropertyUtil.class.getResourceAsStream("/properties/message_ZH.properties"); * in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream("properties/message_ZH.properties"); */ synchronized static private void loadProps( boolean isZh) { logger.debug( "start loading properties" ); InputStream in = null ; if (isZh) { propsZH = new Properties(); name = "properties/message_ZH.properties" ; in = PropertiesReaderUtil. class .getClassLoader().getResourceAsStream(name); } else { propsCN = new Properties(); name = "properties/message_EN.properties" ; in = PropertiesReaderUtil. class .getClassLoader().getResourceAsStream(name); } try { if (isZh) { propsZH.load( new InputStreamReader(in, ENCODING)); } else { propsCN.load( new InputStreamReader(in, ENCODING)); } } catch (Exception e) { logger.debug( "loading properties error :{}" , e); } finally { try { if ( null != in) { in.close(); } } catch (IOException e) { logger.debug( "closing properties io error :{}" , e); } } } public static String getProperty(String key) { return getPropertyZH(key); } public static String getProperty(String key, String defaultValue) { return getPropertyZH(key, defaultValue); } public static String getPropertyZH(String key) { if ( null == propsZH) { loadProps( true ); } return propsZH.getProperty(key); } public static String getPropertyZH(String key, String defaultValue) { if ( null == propsZH) { loadProps( true ); } return propsZH.getProperty(key, defaultValue); } public static String getPropertyCN(String key) { if ( null == propsCN) { loadProps( false ); } return propsCN.getProperty(key); } public static String getPropertyCN(String key, String defaultValue) { if ( null == propsCN) { loadProps( false ); } return propsCN.getProperty(key, defaultValue); } } |
配置文件message_ZH.properties路徑為:properties/message_ZH.properties
也可添加國家化英文或者是其他語言配置
1001=用戶未登錄
#====非業(yè)務(wù)返回碼=========
1100=服務(wù)器內(nèi)部錯誤
1101=空指針異常
1102=數(shù)據(jù)類型轉(zhuǎn)換異常
1103=IO異常
1104=該方法找不到異常
1105=數(shù)組越界異常
1106=請求體缺失異常
1107=類型匹配異常
1108=請求參數(shù)缺失異常
1109=請求方法不支持異常
1110=請求頭類型不支持異常
1111=參數(shù)解析異常
1112=必要參數(shù)不能為空
#=======================
統(tǒng)一異常捕捉類RestfulExceptionHandler此時,基本能保證增強Controller層的返回值了,如果有需要的話,可能通過@RestControllerAdvice注解,針對拋出的異常使用返回值對象進行包裝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
|
package com.study.auth.exception; import com.alibaba.fastjson.JSONException; import com.study.auth.comm.PropertiesReaderUtil; import com.study.auth.comm.ResponseData; import com.study.auth.constant.CommonConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.TypeMismatchException; import org.springframework.boot.json.JsonParseException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.security.auth.login.AccountException; import java.io.IOException; import java.sql.SQLException; /** * @Package: com.study.auth.exception * @Description: <所有異常攔截類> * @Author: milla * @CreateDate: 2020/09/04 15:35 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:35 * @UpdateRemark: <> * @Version: 1.0 */ @Slf4j @RestControllerAdvice public class RestfulExceptionHandler { private ResponseData responseData(String code, Exception e) { log.error( "異常代碼:{},異常描述:{},異常堆棧:" , code, PropertiesReaderUtil.getProperty(code), e); return ResponseData.error(code); } private ResponseData<String> responseData(String code, String message, Exception e) { log.error( "異常代碼:{},異常描述:{},異常堆棧:" , code, message, e); return ResponseData.error(code, message); } /** * 運行時異常 * * @param e 異常 * @return */ @ExceptionHandler (Exception. class ) public ResponseData runtimeExceptionHandler(Exception e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e); } /** * 處理SQLSyntaxErrorException * * @param e 異常 * @return */ @ExceptionHandler (SQLException. class ) public ResponseData<String> sqlException(SQLException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 處理CustomerMessageException * * @param e 異常 * @return */ @ExceptionHandler (CustomMessageException. class ) public ResponseData<String> customerMessageException(CustomMessageException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 處理AccountException * * @param e 異常 * @return */ @ExceptionHandler (AccountException. class ) public ResponseData<String> accountException(AccountException e) { return responseData(e.getMessage(), e); } //---------------------------------------jdk/spring自帶的異常---------------------------------- /** * 處理IllegalArgumentException * * @param e 異常 * @return */ @ExceptionHandler (IllegalArgumentException. class ) public ResponseData<String> illegalArgumentException(IllegalArgumentException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 空指針異常 * * @param e 異常 * @return */ @ResponseStatus @ExceptionHandler (NullPointerException. class ) public ResponseData nullPointerExceptionHandler(NullPointerException e) { return responseData(CommonConstant.EX_NULL_POINTER_EXCEPTION, e); } /** * 類型轉(zhuǎn)換異常 * * @param e 異常 * @return */ @ExceptionHandler (ClassCastException. class ) public ResponseData classCastExceptionHandler(ClassCastException e) { return responseData(CommonConstant.EX_CLASS_CAST_EXCEPTION, e); } /** * IO異常 * * @param e 異常 * @return */ @ExceptionHandler (IOException. class ) public ResponseData iOExceptionHandler(IOException e) { return responseData(CommonConstant.EX_IO_EXCEPTION, e); } /** * 未知方法異常 * * @param e 異常 * @return */ @ExceptionHandler (NoSuchMethodException. class ) public ResponseData noSuchMethodExceptionHandler(NoSuchMethodException e) { return responseData(CommonConstant.EX_NO_SUCH_METHOD_EXCEPTION, e); } /** * 數(shù)組越界異常 * * @param e 異常 * @return */ @ExceptionHandler (IndexOutOfBoundsException. class ) public ResponseData indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException e) { return responseData(CommonConstant.EX_INDEX_OUT_OF_BOUNDS_EXCEPTION, e); } /** * 請求body缺失異常 * * @param e 異常 * @return */ @ExceptionHandler ({HttpMessageNotReadableException. class }) public ResponseData requestNotReadable(HttpMessageNotReadableException e) { return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e); } /** * 類型匹配異常 * * @param e 異常 * @return */ @ExceptionHandler ({TypeMismatchException. class }) public ResponseData requestTypeMismatch(TypeMismatchException e) { return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e); } /** * 方法不支持異常 * * @param e 異常 * @return */ @ExceptionHandler ({HttpRequestMethodNotSupportedException. class }) public ResponseData methodNotSupported(HttpRequestMethodNotSupportedException e) { return responseData(CommonConstant.EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, e); } /** * 請求頭不支持異常 * * @param e 異常 * @return */ @ExceptionHandler ({HttpMediaTypeNotSupportedException. class }) public ResponseData mediaTypeNotAcceptable(HttpMediaTypeNotSupportedException e) { return responseData(CommonConstant.EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION, e); } /** * 參數(shù)解析異常 * * @param e 異常 * @return */ @ExceptionHandler (JSONException. class ) public ResponseData runtimeExceptionHandler(JSONException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 參數(shù)解析異常 * * @param e 異常 * @return */ @ExceptionHandler (JsonParseException. class ) public ResponseData runtimeExceptionHandler(JsonParseException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 請求參數(shù)缺失異常 * * @param e 異常 * @return */ @ExceptionHandler ({MissingServletRequestParameterException. class }) public ResponseData requestMissingServletRequest(MissingServletRequestParameterException e) { return responseData(CommonConstant.EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, e); } /** * 參數(shù)不能為空 * * @param e 異常 * @return */ @ExceptionHandler (MethodArgumentNotValidException. class ) public ResponseData exceptionHandler(MethodArgumentNotValidException e) { return responseData(CommonConstant.PARAMS_IS_NULL, e); } } |
常量類 CommonConstant
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
package com.study.auth.constant; /** * @Package: com.study.auth.constant * @Description: <公共常量類> * @Author: milla * @CreateDate: 2020/09/04 15:37 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:37 * @UpdateRemark: <> * @Version: 1.0 */ public final class CommonConstant { /** * 當(dāng)前用戶名稱 */ public static final String C_CURRENT_ACCOUNT = "current_account" ; /** * 用戶未登錄 */ public static final String EX_NO_TOKEN_EXCEPTION = "1001" ; //--------------------------------非業(yè)務(wù)返回碼--------------------------------------- /** * 運行時異常 */ public static final String EX_RUN_TIME_EXCEPTION = "1100" ; /** * 空指針異常 */ public static final String EX_NULL_POINTER_EXCEPTION = "1101" ; /** * 數(shù)據(jù)轉(zhuǎn)換異常 */ public static final String EX_CLASS_CAST_EXCEPTION = "1102" ; /** * IO異常 */ public static final String EX_IO_EXCEPTION = "1103" ; /** * 找不到該方法異常 */ public static final String EX_NO_SUCH_METHOD_EXCEPTION = "1104" ; /** * 數(shù)組越界異常 */ public static final String EX_INDEX_OUT_OF_BOUNDS_EXCEPTION = "1105" ; /** * 請求體缺失異常 */ public static final String EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION = "1106" ; /** * TYPE匹配異常 */ public static final String EX_TYPE_MISMATCH_EXCEPTION = "1107" ; /** * 請求參數(shù)丟失 */ public static final String EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION = "1108" ; /** * 請求方法類型不支持異常 */ public static final String EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION = "1109" ; /** * MEDIA 類型不支持異常 */ public static final String EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION = "1110" ; /** * 參數(shù)解析異常 */ public static final String PARAMS_PARSE_EXCEPTION = "1111" ; /** * 參數(shù)不能為空 */ public static final String PARAMS_IS_NULL = "1112" ; //----------------------------------------------------------------------------------- } |
自定義異常類 CustomMessageException
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
package com.study.auth.exception; /** * @Package: com.study.auth.exception * @Description: <自定義異常類> * @Author: MILLA * @CreateDate: 2019/8/15 18:39 * @UpdateUser: MILLA * @UpdateDate: 2019/8/15 18:39 * @UpdateRemark: <> * @Version: 1.0 */ public class CustomMessageException extends RuntimeException { public CustomMessageException() { super (); } public CustomMessageException(String message) { super (message); } public CustomMessageException(String message, Throwable cause) { super (message, cause); } public CustomMessageException(Throwable cause) { super (cause); } protected CustomMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super (message, cause, enableSuppression, writableStackTrace); } } |
所需依賴因為使用了阿里的fastJson工具類還需要進入該類的依賴
1
2
3
4
5
|
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version> 1.2 . 58 </version> </dependency> |
至此,可以愉快的使用該返回值的增強類了,在為服務(wù)中,還以將該代碼重構(gòu)到comm中,供多個服務(wù)共同使用,避免重復(fù)早輪子
到此這篇關(guān)于關(guān)于Controller 層返回值的公共包裝類的問題的文章就介紹到這了,更多相關(guān)Controller 層返回值包裝類內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/hu10131013/article/details/108453535