后台服务化之后,服务之间的调用变得很频繁,每一个接口的调用会有多个不同的返回值,有些返回值我们可以直接在后台处理,有些返回值我应该返回到终端用户进行处理。
这就要求我们在处理返回值和异常的时候需要仔细考虑了。
我的处理方式是:
1、我们可以在后台处理的返回信息,就用方法返回值处理,不用抛出异常的方式返回。
比如,用户登录的时候,去查询用户中心,没有查到用户记录,用户中心返回null, 这个时候我们就要自己处理这个返回值,应该是打个日志,然后给终端返回登录信息错误的提示。
2、我们在后台处理不了,必须抛到终端用户发起请求的地方进行处理的情况我们就用抛出异常的方式。然后我们在调用接口的地方捕捉异常,然后统一打日志,然后直接输出错误提示到终端。
第一点没有任何问题,相信多数都这么做的,但是第二点就有点麻烦了。如果我们在每个地方都抛异常,类似于jdk里面,对每一种错误都定义一个异常,那我们得定义多少异常呢。当然,如果你愿意定义这么多异常也可以。只是会比较麻烦而已。
我的解决办法如下:
我们在输出数据到终端的时候格式一般是 {code:1,data:"",msg:"success"} 这样子的。那我们在调用服务化的接口时抛出的异常我们就想办法让它自动转化成这种格式。下面看代码。
第一步,定义少量的异常,我这里只对每个服务定义一个异常类。比如,对整个用户中心就只定义一个异常类。然后所有用户中心服务返回时如果有异常抛出,都使用这一个异常类。
package com.***.common.exception;
import java.io.IOException;
import java.io.Serializable;
public class UserServiceException extends IOException implements Serializable {
private static final long serialVersionUID = 5848657787701620781L;
private String code;
private String codeMsg;
public UserServiceException(String code, String codeMsg, final String exceptinMessage) {
super(exceptinMessage);
this.code = code;
this.codeMsg = codeMsg;
}
public UserServiceException(int code, String codeMsg, final String message) {
super(message);
this.code = code + "";
this.codeMsg = codeMsg;
}
public UserServiceException(int code, String codeMsg) {
super(codeMsg);
this.code = code + "";
this.codeMsg = codeMsg;
}
public String getCode() {
return code;
}
public String getCodeMsg() {
return codeMsg;
}
}
第二步,调用接口的代码。
/**
* 验证码登录
* @return
*/
@ResponseBody
@RequestMapping(value="codeLogin", method = {RequestMethod.POST,RequestMethod.GET})
public Object codeLogin(HttpServletRequest request, HttpServletResponse response,String mobile, String code){
if(StringUtil.trimToNull(mobile)==null
||StringUtil.trimToNull(code)==null){
log.info("登录参数不正确,mobile={},code={}",mobile,code);
return new DataResult(DataCodes.PARAMETER_EMPTY_ERROR, DataCodes.PARAMETER_EMPTY_MSG);
}
try {
//接口调用
UserApi user = userApiService.codeLogin(mobile,code);
if(user==null){
return new DataResult(DataCodes.CODE_ERROR,DataCodes.CODE_ERROR_MSG);
}
writeCookie(request, response, user.getToken());
Map<String,Object> map = new HashMap<String,Object>();
map.put("nickname", user.getNickname());
map.put("head", user.getPhoto());
map.put("status", user.getStatus());
return new DataResult(map);
}
catch (UserServiceException e) {
//用户中心可能抛出不同的错误信息,但是都是利用同一个异常类抛出,关键在于里面的code是什么。
log.info("登录调用service出错,message={}",e.getMessage());
//异常信息里面带有code,可以直接输出到终端,终端会看到提示和code代码。
return new DataResult(e.getCode(), e.getCodeMsg());
}
catch (Exception e) {
log.error("登录错误",e);
//这个异常捕捉必须要有
return new DataResult(DataCodes.SYSTEM_ERROR, DataCodes.SYSTEM_ERROR_MSG);
}
}
第三步, 接口返回不同信息给接口调用者。记住自己要写一个常量类,用来保存所有的code和msg。然后正常返回和错误返回都是调用同一个常量类的code和msg。
@Override
public UserApi codeLogin(String mobile, String code) throws UserServiceException {
String loginCode = (String)redisTemplate.opsForValue().get(RedisKeys.LOGIN_KEY+mobile);
if(loginCode==null || !loginCode.equals(code)){
log.error("验证码不存在,mobile={},code={},loginCode={}",mobile,code,loginCode);
//错误返回,实例化一个异常类的对象,并且设置好code和msg,返回抛出。
throw new UserServiceException(DataCodes.CODE_ERROR,DataCodes.CODE_ERROR_MSG,DataCodes.CODE_ERROR_MSG);
}
//清除验证码
redisTemplate.delete(RedisKeys.LOGIN_KEY+mobile);
UserDbo userDb = userDao.getUserByMobile(mobile);
if(userDb==null){
log.error("验证码登录失败,mobile不存在,mobile={},code={},loginCode={}",mobile,code,loginCode);
//错误返回,实例化一个异常类的对象,并且设置好code和msg,返回抛出。
throw new UserServiceException(DataCodes.MOBILE_NOT_EXIST_ERROR,DataCodes.MOBILE_NOT_EXIST_ERROR_MSG,DataCodes.MOBILE_NOT_EXIST_ERROR_MSG);
}
//更新 token
long tokenTime = System.currentTimeMillis();
String token =。。。。。;//省略掉了
//异步更新
executeUpdateToken.execute(new UpdateTokenThread(userDb.getId(), token, tokenTime, userExtDao,true));
UserApi user = new UserApi();
// user.setToken(token);
user.setAccount(userDb.getAccount());
user.setStatus(userDb.getStatus());
user.setNickname(userDb.getNickname());
return user;//正常返回
}
这样,我们就可以处理各种不同返回的情况了,而且只定义少量的异常类,却能够满足所有的接口调用的返回处理。