SaTokenException: 非Web上下文无法获取Request问题解决
因为异步的原因,他会丢掉ThreadLocal中原来线程的数据,从而获取不到loginUser,这种情况下我们可以在方法内的局部变量中先保存原来线程的信息,在异步编排的新线程中拿着局部变量的值重新设置到新线程中即可。
背景
有个接口一直无法通过验证。
跟踪了下,发现是在获取用户ID的时候,直接抛了异常。
使用的是sa-token
代码如下:
public Integer getUserId{
if (StpUtil.isLogin()) {
...
return Integer.parseInt(StpUtil.getLoginId().toString());
}
...
}
跟踪代码的时候,在StpUtil.isLogin()这里抛出了SaTokenException的问题。
源码分析
跟踪了下代码,出错代码定位如下:
public class SpringMVCUtil {
/**
* 获取当前会话的 request
* @return request
*/
public static HttpServletRequest getRequest() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new SaTokenException("非Web上下文无法获取Request");
}
return servletRequestAttributes.getRequest();
}
/**
* 获取当前会话的 response
* @return response
*/
public static HttpServletResponse getResponse() {
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if(servletRequestAttributes == null) {
throw new SaTokenException("非Web上下文无法获取Response");
}
return servletRequestAttributes.getResponse();
}
}
我们发现,因为无法获取到ServletRequestAttributes对象。所以报错了。
ServletRequestAttributes对象获取不到的情况一般分为以下几种情况:
1、异步线程调用。例如@Async注解的情况
2、子线程调用的情况。由于RequestContextHolder使用ThreadLocal共享数据,子线程会导致丢掉ThreadLocal中原来线程的数据。
3、非Web上下文的情况下调用。例如rpc框架这种。
查看了下代码,发现是使用了CompletableFuture的子线程方式,导致在RequestContextHolder中获取不到对象。
解决方法
解决方法比较简单:
1、参数透传
修改接口,把需要在ThreadLocal中获取的参数透传到其它方法中。
2、重置上下文对象
在子线程中重置RequestContextHolder对象。
// 从主线程获取用户数据 放到局部变量中
RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
CompletableFuture<Void> testFuture = CompletableFuture.runAsync(() -> {
// 把旧RequestAttributes放到新线程的RequestContextHolder中
RequestContextHolder.setRequestAttributes(attributes);
...
}
使用方式1 测试了下,发现问题圆满解决。
总结
来张网络图片
因为异步的原因,他会丢掉ThreadLocal中原来线程的数据,从而获取不到loginUser,这种情况下我们可以在方法内的局部变量中先保存原来线程的信息,在异步编排的新线程中拿着局部变量的值重新设置到新线程中即可。
更多推荐
所有评论(0)