1、服务器返回格式
系统统一返回JSON数据格式:
success: {"status":"success","data":{"name":"test"}}
fail:{"status":"fail","errorCode":900001,"errorMsg":"参数错误"}
备注:请求成功,服务器返回 status状态位以及主要报文体data;请求失败,则返回status状态位、错误code码以及错误含义,不返回主要报文data(可选、可控),客户端通过解析status判断请求是否成功,然后做出相应的动作。
2、服务器返回数据工具实体辅助类
response 顶层类:
public class Response implements Serializable {
private static final long serialVersionUID = 1L;
protected transient String SUCCESS_STATUS = "success";
protected transient String FAIL_STATUS = "fail";
private String status;
public Response() {
this.status = SUCCESS_STATUS;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
成功:
public class SuccessResponse extends Response {
private static final long serialVersionUID = 1L;
private Object data;
public SuccessResponse(Object data) {
this.data = data;
this.setStatus(SUCCESS_STATUS);
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
失败:
public class FailResponse extends Response {
private static final long serialVersionUID = 1L;
private Integer errorCode;
private String errorMsg;
public FailResponse() {
}
public FailResponse(Integer errorCode, String errorMsg) {
this.errorCode = errorCode;
this.errorMsg = errorMsg;
this.setStatus(FAIL_STATUS);
}
public Integer getErrorCode() {
return errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
response响应工具辅助类:
public class ResponseUtil {
public static Response success() {
return new Response();
}
public static Response success(Object data) {
return new SuccessResponse(data);
}
public static Response fail(Integer errorCode, String errorMsg) {
return new FailResponse(errorCode, errorMsg);
}
}
3、系统异常定义,使用enum存储
public enum ErrorType {
SYSTEM_ERROR(800,"系统错误","system error"),
NO_USER(90003,"该用户不存在","user does not exist"),
REGISTERED_PHONE(90004,"手机号已被注册","phone number has been registered"),
;
private Integer code;
private String zhCnMsg;
private String enMsg;
ErrorType(Integer code, String zhCnMsg, String enMsg) {
this.code = code;
this.zhCnMsg = zhCnMsg;
this.enMsg = enMsg;
}
public static ErrorType get(Integer code) {
for (ErrorType errorType : ErrorType.values()) {
if (errorType.getCode().equals(code)) {
return errorType;
}
}
return null;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getZhCnMsg() {
return zhCnMsg;
}
public void setZhCnMsg(String zhCnMsg) {
this.zhCnMsg = zhCnMsg;
}
public String getEnMsg() {
return enMsg;
}
public void setEnMsg(String enMsg) {
this.enMsg = enMsg;
}
}
全局异常-->BSExceptions:
public class BSException extends RuntimeException {
private static final long serialVersionUID = 1L;
private ErrorType errorType;
public BSException () {
}
public BSException (String message) {
super(message);
}
public BSException (Throwable cause) {
super(cause);
}
public BSException (ErrorType errorType) {
this.errorType = errorType;
}
public BSException (ErrorType errorType, String message) {
super(message);
this.errorType = errorType;
}
public static BSException error(ErrorType error) {
return new BSException (error);
}
public static BSException error(ErrorType error, String message) {
return new BSException (error, message);
}
public static BSException systemError() {
return new BSException (ErrorType.SYSTEM_ERROR);
}
public ErrorType getErrorType() {
return errorType;
}
public void setErrorType(ErrorType errorType) {
this.errorType = errorType;
}
}
4、使用(不使用spring异常体系,抓取不全)
以上为系统设计数据结构相关,项目设计一个Filter主入口,定义在web.xml中,url-pattern-> /*,
doFilter()->执行处理相关业务逻辑:
public class BStFilter implements Filter {
//slf4j--log4j2
private Logger logger = LoggerFactory.getLogger(getClass());
private final String ASSETS_PREFIX = "/assets/**";
private PathMatcher pathMatcher;
private DefaultServletHttpRequestHandler resourceHandler;
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
String servletPath = request.getServletPath();
String contextPath = request.getContextPath();
request.setAttribute("ctx", contextPath);
boolean process = true;
try {
if (assets(servletPath)) {
//静态资源直接交给web容器处理,不用再进入spring-->mvc:resource
resourceHandler.handleRequest(request, response);
return;
} else {
/**处理一些业务逻辑,如权限等认证**/
if (process) {
chain.doFilter(request, response);
}
} catch (Exception e) {
//处理系统内部出现的各种异常
doException(request, response, e);
} finally {
reset();
}
}
/**
* 判断是否为静态资源
*
* @param path
* @return
*/
protected boolean assets(String path) {
return match(ASSETS_PREFIX, path);
}
private boolean match(String pattern, String path) {
return pathMatcher.match(pattern, path);
}
/**
*
* @param request
* @param response
* @param e
*/
protected void doException(HttpServletRequest request, HttpServletResponse response, Exception e) {
ErrorType errorType = null;
//org.apache.commons.lang3.exception.ExceptionUtils;
Throwable rootException = ExceptionUtils.getRootCause(e);
String extMsg = null;
if (rootException instanceof BSException) {
BSException ex = (BSException) rootException;
errorType = ex.getErrorType();
extMsg = ex.getMessage();
logException(rootException, errorType);
} else {
//e.printStackTrace();
errorType = ErrorType.SYSTEM_ERROR;
}
Integer code = errorType.getCode();
String msg = errorType.getZhCnMsg();
if (StringUtils.isNotBlank(extMsg)) {
msg = msg.concat(":").concat(extMsg);
}
WebUtil.renderJson(response, ResponseUtil.fail(code, msg));
return;
}
/**
*
* @param throwable
* @param errorType
*/
protected void logException(Throwable throwable, ErrorType errorType) {
String className = throwable.getStackTrace()[1].getClassName();
String methodName = throwable.getStackTrace()[1].getMethodName();
int lineNumbe = throwable.getStackTrace()[1].getLineNumber();
String nowTime = DateTimeUtil.getNowTime();
Integer code = errorType.getCode();
String enMsg = errorType.getEnMsg();
String zhCnMsg = errorType.getZhCnMsg();
String extMsg = throwable.getMessage();
if (StringUtils.isNotBlank(extMsg)) {
zhCnMsg = zhCnMsg.concat(":").concat(extMsg);
}
String classLog = "{} exception at:{}.{}:{}";
String typeLog = "{} errorType:{code:{},enMsg:{},cnMsg:{}}";
logger.error(classLog, nowTime, className, methodName, lineNumbe);
logger.error(typeLog, nowTime, code, enMsg, zhCnMsg);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
ServletContext context = filterConfig.getServletContext();
//org.springframework.util.PathMatcher;
//org.springframework.util.AntPathMatcher;
pathMatcher = new AntPathMatcher();//spring Ant风格匹配处理器
//spring 内部默认servletHandler
//参考org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler
resourceHandler = new DefaultServletHttpRequestHandler();
resourceHandler.setServletContext(context);
}
@Override
public void destroy() {
}
private void reset() {
//释放一些资源
}
}
public abstract class WebUtil {
private static String CHARSET = Constants.DEFAULT_CHARSET_NAME;
public static void renderJson(HttpServletResponse response, Object obj) {
renderJson(response, obj, CHARSET);
}
public static void renderJson(HttpServletResponse response, Object data, String charset) {
try {
render(response, JsonUtil.toJson(data), "Content-Type", "text/json;charset=" + CHARSET);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
public static void render(HttpServletResponse response, String content, String headerName, String headerValue) {
response.addHeader(headerName, headerValue);
response.setStatus(200);
render(response, content);
}
private static void render(HttpServletResponse response, String content) {
try {
PrintWriter pw = response.getWriter();
pw.write(content);
pw.flush();
pw.close();
return;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
//方便替换其它json工具类,jackson或fastjson
//更换此类的json工具,外部不用改变代码,便于整体更换json框架
public abstract class JsonUtil {
//com.alibaba.fastjson.TypeReference
//com.alibaba.fastjson.parser.Feature
//com.alibaba.fastjson.serializer.SerializerFeature
private static SerializerFeature[] serializerFeatures;
private static Feature[] features;
static {
serializerFeatures = new SerializerFeature[] { SerializerFeature.WriteMapNullValue,
SerializerFeature.WriteNullListAsEmpty, SerializerFeature.WriteNullStringAsEmpty,
SerializerFeature.WriteNullNumberAsZero, SerializerFeature.DisableCircularReferenceDetect };
features = new Feature[] {
};
}
/**
* 序列化
*
* @param obj
* @return
*/
public static String toJson(Object obj) {
//com.alibaba.fastjson.JSON
return JSON.toJSONString(obj, serializerFeatures);
}
}
public abstract class Constants {
public static Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
public static final String DEFAULT_CHARSET_NAME = DEFAULT_CHARSET.name();
}
public abstract class DateTimeUtil {
/**
* yyyy-MM-dd HH:mm:ss
*
* @return
*/
public static String getNowTime() {
//org.joda.time.DateTime
return DateTime.now().toString(""yyyy-MM-dd HH:mm:ss"");
}
}
5、内部流转调用
以上客户端请求有入口filter进入,而后进入系统内部,故内部抛出的各种异常都能捕获,再有入口filter处理,返回客户端统一格式响应报文。
filter-->doFilter()-->chain.doFilter(request, response);执行到springMVC系统内部,controller-->service-->dao
@Controller
@RequestMapping("/bs")
public class BSController{
@Autowired
private BSService bSService;
@PostMapping("/test")
public Response test() {
bSService.test();
return ResponseUtil.success();
}
@PostMapping("/test1")
public Response test1() {
return ResponseUtil.success(bSService.test1(););
}
}
@Service
public class BSServiceImpl implements BSService {
@Autowired
private BSDAO bSDAO;
@Override
public void test() {
String phone = "";
String password ="";
//不存在胡写的一个类,主要模拟用户查找
Test test = bSDAO.findByPhone(phone);
if (test != null) {
//用户不存在抛对应存储异常,传递到入口filter,而后执行doException,返回客户端标准json
throw BSException .error(ErrorType.REGISTERED_PHONE);
}
//异常抛完并处理,底下的业务逻辑不走了
//处理业务逻辑
}
@Override
public TestResponse test2() {
TestResponse response = new TestResponse();
Test test = bSDAO.findById(1);
if (test != null) {
//org.springframework.beans.BeanUtils
BeanUtils.copyProperties(test, response);
}else{
//用户不存在抛对应存储异常,传递到入口filter,而后执行doException,返回客户端标准json
throw BSException .error(ErrorType.NO_USER, "自定义其它错误说明");
}
return response;
}
}
6、解释
进入系统业务处理内部,一些胡求定义的不存在的类,是为了说明一些业务逻辑。总体讲来就是,利用异常体系处理各种业务返回错误码,入口filter处理了所有(除了自身不可预测的错误)的错误返回码,正确的响应有springMVC json 处理返回客户端,其中其它含义原理由读者自行体会,希望对有需要的人有帮助。