如何解决Controller层返回值的公共包装类的问题
发表于:2024-11-28 作者:千家信息网编辑
千家信息网最后更新 2024年11月28日,本篇文章为大家展示了如何解决Controller层返回值的公共包装类的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。场景:在微服务中,一般返回数据都会有个
千家信息网最后更新 2024年11月28日如何解决Controller层返回值的公共包装类的问题
本篇文章为大家展示了如何解决Controller层返回值的公共包装类的问题,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。
场景:在微服务中,一般返回数据都会有个返回码、返回信息和返回消息体,但是每次返回时候调用或者是封装,太过麻烦,有没有什么办法不用每次都封装呢?
答案是有的。
返回值对象 ResponseData
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: <返回数据> * @Author: MILLA * @CreateDate: 2018/4/8 9:10 * @UpdateUser: MILLA * @UpdateDate: 2018/4/8 9:10 * @Version: 1.0 */public final class ResponseDataimplements Serializable { private static final long serialVersionUID = 7824278330465676943L; private static final String SUCCESS_CODE = "1000"; private static final String SUCCESS_MSG = "success"; /** * 响应编码 */ @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 1) private String code; /** * 响应提示 */ @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 2) private String msg; /** * 返回的数据 */ @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 ResponseData success(T t) { return initData(SUCCESS_CODE, SUCCESS_MSG, t); } public static ResponseData errorData(String code, T data) { String msg = PropertiesReaderUtil.getProperty(code, null); return initData(code, msg, data); } public static ResponseData errorData(String code, String msg, T data) { return initData(code, msg, data); } private static 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通过实现InitializingBean和装饰者模式对Controller层的返回值进行包装,大致思路:
通过RequestMappingHandlerAdapter获取所有的返回值处理对象HandlerMethodReturnValueHandler创建一个新的集合存储上一步获取的集合(因为上一步的结果是unmodifiableList类型的)遍历该集合找到HandlerMethodReturnValueHandler对象,将这个位置的handler替换程自定义的handler将新获到的集合重新设置到RequestMappingHandlerAdapter的setReturnValueHandlers方法中
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 */@Configurationpublic class InitializingAdviceDecorator implements InitializingBean { @Autowired private RequestMappingHandlerAdapter adapter; @Override public void afterPropertiesSet() { //获取所有的handler对象 ListreturnValueHandlers = adapter.getReturnValueHandlers(); //因为上面返回的是unmodifiableList,所以需要新建list处理 List handlers = new ArrayList(returnValueHandlers); this.decorateHandlers(handlers); //将增强的返回值回写回去 adapter.setReturnValueHandlers(handlers); } /** * 使用自定义的返回值控制类 * * @param handlers */ private void decorateHandlers(List 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 { //如果已经封装了结构体就直接放行 if (returnValue instanceof ResponseData) { handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); return; } //正常返回success ResponseData success = ResponseData.success(returnValue); handler.handleReturnValue(success, returnType, mavContainer, webRequest); } }}
配置文件读取类PropertiesReaderUtil
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=用户未登录
#====非业务返回码=========
1100=服务器内部错误
1101=空指针异常
1102=数据类型转换异常
1103=IO异常
1104=该方法找不到异常
1105=数组越界异常
1106=请求体缺失异常
1107=类型匹配异常
1108=请求参数缺失异常
1109=请求方法不支持异常
1110=请求头类型不支持异常
1111=参数解析异常
1112=必要参数不能为空
#=======================
统一异常捕捉类RestfulExceptionHandler此时,基本能保证增强Controller层的返回值了,如果有需要的话,可能通过@RestControllerAdvice注解,针对抛出的异常使用返回值对象进行包装
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@RestControllerAdvicepublic class RestfulExceptionHandler { private ResponseData responseData(String code, Exception e) { log.error("异常代码:{},异常描述:{},异常堆栈:", code, PropertiesReaderUtil.getProperty(code), e); return ResponseData.error(code); } private ResponseDataresponseData(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 sqlException(SQLException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 处理CustomerMessageException * * @param e 异常 * @return */ @ExceptionHandler(CustomMessageException.class) public ResponseData customerMessageException(CustomMessageException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 处理AccountException * * @param e 异常 * @return */ @ExceptionHandler(AccountException.class) public ResponseData accountException(AccountException e) { return responseData(e.getMessage(), e); } //---------------------------------------jdk/spring自带的异常---------------------------------- /** * 处理IllegalArgumentException * * @param e 异常 * @return */ @ExceptionHandler(IllegalArgumentException.class) public ResponseData 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); } /** * 类型转换异常 * * @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); } /** * 数组越界异常 * * @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); } /** * 参数解析异常 * * @param e 异常 * @return */ @ExceptionHandler(JSONException.class) public ResponseData runtimeExceptionHandler(JSONException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 参数解析异常 * * @param e 异常 * @return */ @ExceptionHandler(JsonParseException.class) public ResponseData runtimeExceptionHandler(JsonParseException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 请求参数缺失异常 * * @param e 异常 * @return */ @ExceptionHandler({MissingServletRequestParameterException.class}) public ResponseData requestMissingServletRequest(MissingServletRequestParameterException e) { return responseData(CommonConstant.EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, e); } /** * 参数不能为空 * * @param e 异常 * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseData exceptionHandler(MethodArgumentNotValidException e) { return responseData(CommonConstant.PARAMS_IS_NULL, e); }}
常量类 CommonConstant
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 { /** * 当前用户名称 */ public static final String C_CURRENT_ACCOUNT = "current_account"; /** * 用户未登录 */ public static final String EX_NO_TOKEN_EXCEPTION = "1001"; //--------------------------------非业务返回码--------------------------------------- /** * 运行时异常 */ public static final String EX_RUN_TIME_EXCEPTION = "1100"; /** * 空指针异常 */ public static final String EX_NULL_POINTER_EXCEPTION = "1101"; /** * 数据转换异常 */ 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"; /** * 数组越界异常 */ 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"; /** * 请求参数丢失 */ 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"; /** * 参数解析异常 */ public static final String PARAMS_PARSE_EXCEPTION = "1111"; /** * 参数不能为空 */ public static final String PARAMS_IS_NULL = "1112"; //-----------------------------------------------------------------------------------}
自定义异常类 CustomMessageException
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工具类还需要进入该类的依赖
com.alibaba fastjson 1.2.58
至此,可以愉快的使用该返回值的增强类了,在为服务中,还以将该代码重构到comm中,供多个服务共同使用,避免重复早轮子
上述内容就是如何解决Controller层返回值的公共包装类的问题,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注行业资讯频道。
参数
类型
对象
方法
包装
处理
支持
缺失
数据
文件
服务
代码
指针
数组
模式
用户
封装
配置
问题
内容
数据库的安全要保护哪些东西
数据库安全各自的含义是什么
生产安全数据库录入
数据库的安全性及管理
数据库安全策略包含哪些
海淀数据库安全审计系统
建立农村房屋安全信息数据库
易用的数据库客户端支持安全管理
连接数据库失败ssl安全错误
数据库的锁怎样保障安全
数据库原理与应用实验
软件开发的电脑
数据库中什么是表
杨明非网络安全专家
数据库十场占有率
深圳千阳网络技术有限公司官网
软件开发团队第一梯队建设
帝国神话创建服务器数据
软件开发Web浏览器简称为
晋城网络技术价格
让世界为我骄傲网络安全宣传
吉林网络安全信睿
游戏论坛数据库课设
儿童网络技术启蒙
网络安全路由器的作用是什么
护苗网络安全课系列视频最新
led大屏服务器
数据库中有学生表课程表
游戏服务器怎么搭建
云服务器同时在线人数
数据库的技术问题及解决方案
四级数据库工程师能评职称吗
翼城县软件开发
mql 删除多个数据库
鲲鹏服务器基础架构
如何查安卓APP服务器的位置
光学跟踪软件开发
连接上级局域网的服务器
南京渔网互联网科技有限公司
服务器和网站空间的关系