关于Controller 层返回值的公共包装类的问题

网友投稿 1108 2022-12-05

关于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 ResponseData implements 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

*/

@Configuration

publichttp:// class InitializingAdviceDecorator implements InitializingBean {

@Autowired

private RequestMappingHandlerAdapter adapter;

@Override

public void afterPropertiesSet() {

//获取所有的handler对象

List returnValueHandlers = 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

@RestControllerAdvice

public class RestfulExceptionHandler {

private ResponseData responseData(String code, Exception e) {

log.error("异常代码:{},异常描述:{},异常堆栈:", code, PropertiesReaderUtil.getProperty(code), e);

return ResponseData.error(code);

}

private ResponseData 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 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中,供多个服务共同使用,避免重复早轮子

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:使用dom4j递归解析节点内还含有多个节点的xml
下一篇:Dom4j解析xml复杂多节点报文方式
相关文章

 发表评论

暂时没有评论,来抢沙发吧~