首先贴上今天发现的牛皮的springcloud文章
https://blog.csdn.net/qq_42046105
使用springcloud框架feign组件时:
1 标识是前端调用还是feign内部调用的解决办法:自定义FeignDefaultConfiguration类,实现RequestInterceptor接口中的apply方法,该接口会拦截feign调用内部服务的请求,首先根据项目需求把字段重新设置一遍,比如token;然后在请求头里面设置一个标识当前请求为内部请求的值即可。
@Configuration
@Slf4j
public class FeignDefaultConfiguration implements RequestInterceptor {
/**
* 携带token参数调用feign服务
*
* @param requestTemplate
*/
@Override
public void apply(RequestTemplate requestTemplate) {
try {
//当前请求
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
//根据当前请求获取token
String tokenFromRequest = HttpRequestUtil.getTokenFromRequest(request);
requestTemplate.header("Authorization", "Bearer " + tokenFromRequest);
} catch (Exception e) {
log.error("拦截器封装token失败,URL:"+requestTemplate.url(), e);
} finally {
//区分当前请求是否为内部调用
requestTemplate.header(BaseConstant.FEIGN_SERVER_REQUEST_KEY, "true");
}
}
}
2 feign服务之前调用发生异常时,可以自定义FeignErrorDecoder实现ErrorDecoder接口的decode方法
@Configuration
@Slf4j
public class FeignErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String s, Response response) {
try {
if (response.body() != null) {
String body = Util.toString(response.body().asReader());
Map mapType = JSON.parseObject(body, Map.class);
Object trace = mapType.get("trace");
if (trace != null) {
log.error(trace.toString());
}
String message = mapType.get("message").toString();
String exceptionClass = mapType.get("exception").toString();
Class exClass = Class.forName(exceptionClass);
//根据参数类型获取相应的构造函数
Constructor constructor = exClass.getConstructor(String.class);
Exception ex = (Exception) constructor.newInstance(message);
if(ex instanceof FeignServerException) {
return new ServiceException(ex.getMessage());
}
return ex;
}
return new ServiceException("远程未返回数据");
} catch (Exception e) {
return new ServiceException(e.getMessage());
}
}
}
3 如果服务A调用服务B,B发生异常,处理流程如下(实现准备好全局异常处理类@ControllerAdvice):
a. 异常被B服务的全局异常处理器捕获,判断请求头中是否有标识为内部请求的信息
b. 发现当前请求内部请求时,B服务不处理异常,选择直接抛出异常
c. 异常被第二步中的处理机制处理,封装成对应的异常对象抛到A服务
d. 异常对象被A服务的全局异常处理器捕获,根据异常的种类返回对应信息给前端
@ExceptionHandler(HystrixRuntimeException.class)
@ResponseBody
public Object hystrixRuntimeException(HystrixRuntimeException e) throws Exception {
log.error("调用feign服务业务逻辑异常", e);
Throwable throwable = e.getCause();
if (throwable instanceof RuntimeException) {
RuntimeException runtimeException = (RuntimeException) throwable;
if (runtimeException instanceof BeanValidateException
|| runtimeException instanceof ParseExcelException
|| runtimeException instanceof CheckParamLogicException
|| runtimeException instanceof IllegalArgumentException
) {
return paramValidateExceptionHandler(runtimeException);
}
if (runtimeException instanceof TokenInvalidException) {
return tokenInvalidExceptionHandler(runtimeException);
}
if (runtimeException instanceof ServiceException) {
return serviceException(runtimeException);
}
}
return new ApiResult(ApiResultCodeEnum.E0000);
}
4 项目启动后的初始化操作:自定义类实现CommandLineRunner接口的run方法
5 JWT身份验证流程(JWT介绍请移步http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html)
a 用户输入用户名,密码登陆,后台接收到请求,验证用户信息
b 验证通过后,设置过期时间,用户信息(如username,注意不要吧密码填充进jwt字符串中)等,生成一个jwt字符串标识(3个部分,用.分隔)
c 把jwt字符串存入缓存,用username作为key存入缓存,并放在header中返回给前端
d 前端每次请求都带上这个jwt字符串,后台每次接受到请求的时候,判断请求头中或者cookie中是否有token,如果有,就根据解析出token中的信息,比如username,然后用username从缓存中获取token
e 如果取到了不为空,就放行,并且重新设置redis中的token过期时间,否则就返回验证未通过。
1 在项目中的实体类中,一定要检查有没有set方法,很多莫名其妙的错误或者意料之外的结果都是因为这个
2 在mybatis项目中。字段定义的时候能少用下划线就少用下划线,别等到写xml的时候后悔
3 但实际结构确实截然不同 com.immer.monitor.persistence 是单个文件夹 而 com/immer/monitor/persistence 是一个文件夹嵌套 会 导致资源文件not found 的问题,而且很难排查得到