SaTokenException: 非Web上下文无法获取Request问题解决
Posted 沛沛老爹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SaTokenException: 非Web上下文无法获取Request问题解决相关的知识,希望对你有一定的参考价值。
背景
有个接口一直无法通过验证。
跟踪了下,发现是在获取用户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,这种情况下我们可以在方法内的局部变量中先保存原来线程的信息,在异步编排的新线程中拿着局部变量的值重新设置到新线程中即可。
以上是关于SaTokenException: 非Web上下文无法获取Request问题解决的主要内容,如果未能解决你的问题,请参考以下文章