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问题解决的主要内容,如果未能解决你的问题,请参考以下文章

SpringMVC原理及非注解配置详解

asp.net 自带的缓存

HttpRuntime.Cache与HttpContext.Current.Cache

java ,为啥无法从静态上下文中引用非静态方法

ASP.NET Cache缓存的用法

BFC 块级格式化上下文