RequestContextHolder 很方便的获取 request

在 Spring boot web 中我们可以通过 RequestContextHolder 很方便的获取 request。

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

// 获取 request
HttpServletRequest request = requestAttributes.getRequest();
不再需要通过参数传递 request。在 Spring webflux 中并没提供该功能,使得我们在 Aop 或者一些其他的场景中获取 request 变成了一个奢望???

寻求解决方案
首先我想到的是看看 spring-security 中是否有对于的解决方案,因为在 spring-security 中我们也是可以通过 SecurityContextHolder 很方便快捷的获取当前登录的用户信息。

找到了 ReactorContextWebFilter,我们来看看 security 中他是怎么实现的。 https://github.com/spring-projects/spring-security/blob/master/web/src/main/java/org/springframework/security/web/server/context/ReactorContextWebFilter.java#L43

public class ReactorContextWebFilter implements WebFilter {
private final ServerSecurityContextRepository repository;

public ReactorContextWebFilter(ServerSecurityContextRepository repository) {
Assert.notNull(repository, "repository cannot be null");
this.repository = repository;
}

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
return chain.filter(exchange)
.subscriberContext(c -> c.hasKey(SecurityContext.class) ? c :
withSecurityContext(c, exchange)
);
}

private Context withSecurityContext(Context mainContext, ServerWebExchange exchange) {
return mainContext.putAll(this.repository.load(exchange)
.as(ReactiveSecurityContextHolder::withSecurityContext));
}
}
源码里面我们可以看到 他利用一个 Filter,chain.filter(exchange) 的返回值 Mono 调用了 subscriberContext 方法。 那么我们就去了解一下这个 reactor.util.context.Context。找到 reactor 官方文档中的 名宇娱乐context 章节:https://projectreactor.io/docs/core/release/reference/#context

大意是:从 Reactor 3.1.0 开始提供了一个高级功能,可以与 ThreadLocal 媲美,应用于 Flux 和 Mono 的上下文工具 Context。更多请大家查阅官方文档,对英文比较抵触的朋友可以使用 google 翻译。

mica 中的实现
mica 中的实现比较简单,首先是我们的 ReactiveRequestContextFilter:

/**
* ReactiveRequestContextFilter
*
* @author L.cm
*/
@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.REACTIVE)
public class ReactiveRequestContextFilter implements WebFilter {

@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpRequest request = exchange.getRequest(www.gouyiflb.cn/);
return chain.filter(exchange)
.subscriberContext(ctx -> ctx.put(ReactiveRequestContextHolder.CONTEXT_KEY, request));
}
}
在 Filter 中直接将 request 存储到 Context 上下文中。

ReactiveRequestContextHolder 工具:

/**
* ReactiveRequestContextHolder
*
* @author L.cm
*/
public class ReactiveRequestContextHolder {
static final Class<ServerHttpRequest> CONTEXT_KEY = ServerHttpRequest.class;

/**
* Gets the {@code Mono<ServerHttpRequest>} from Reactor {@link Context}
* @return the {@code Mono<ServerHttpRequest>}
*/
public static Mono<ServerHttpRequest> getRequest() {
return Mono.subscriberContext()
.map(ctx -> ctx.get(CONTEXT_KEY));
}

}
怎么使用呢?
mica 中对未知异常处理,从 request 中获取请求的相关信息
@ExceptionHandler(Throwable.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public Mono<?> handleError(Throwable e) {
log.error("未知异常", e);
// 发送:未知异常异常事件
return ReactiveRequestContextHolder.getRequest()
.doOnSuccess(r -> publishEvent(r, e))
.flatMap(r -> Mono.just(R.fail(SystemCode.FAILURE)));
}

private void publishEvent(ServerHttpRequest request, Throwable error) {
// 具体业务逻辑
}
WebClient 透传 request 中的 header
此示例来源于开源中国问答中笔者的回复: 《如何在gateway 中获取 webflux的 RequestContextHolder》

@GetMapping("/test")
@ResponseBody
public Mono<String> test(www.michenggw.com) {
WebClient webClient = testClient();
return webClient.get().uri("www.suoLaiervip.com").retrieve().bodyToMono(String.class);
}

@Bean
public WebClient testClient() {
return WebClient.builder()
.filter(testFilterFunction())
.baseUrl("https://www.baidu.com")
.build();
}

private ExchangeFilterFunction testFilterFunction() {
return (request, next) -> ReactiveRequestContextHolder.getRequest()
.flatMap(r -> {
ClientRequest clientRequest = ClientRequest.from(request)
.headers(headers -> headers.set(HttpHeaders.USER_AGENT, r.getHeaders().getFirst(HttpHeaders.USER_AGENT)))
.build();
return next.exchange(clientRequest);
});
}
上段代码是透传 web 中的 request 中的 user_agent 请求头到 WebClient 中。

开源推荐

原文地址:https://www.cnblogs.com/qwangxiao/p/10660745.html

时间: 2024-11-10 15:23:46

RequestContextHolder 很方便的获取 request的相关文章

Spring中如何获取request的方法汇总及其线程安全性分析

前言 本文将介绍在Spring MVC开发的web系统中,获取request对象的几种方法,并讨论其线程安全性.下面话不多说了,来一起看看详细的介绍吧. 概述 在使用Spring MVC开发Web系统时,经常需要在处理请求时使用request对象,比如获取客户端ip地址.请求的url.header中的属性(如cookie.授权信息).body中的数据等.由于在Spring MVC中,处理请求的Controller.Service等对象都是单例的,因此获取request对象时最需要注意的问题,便是

Springmvc获取request对象&amp;线程安全

概述:在使用Springmvc开发web系统时,经常要用到request对象来处理请求,比如获取客户端IP地址.请求的url.header中的属性(cookie.授权信息等).body中的数据等.由于Springmvc中的Controller.Service等都是单例的,因此就需要关注request对象是否是线程安全的:当有大量并发请求时,能否保证不同请求/线程中使用不同的request对象.(本文对request的讨论,同样适用于response对象.InputStream/Reader.Ou

springmvc中获取request对象,加载biz(service)的方法

获取request对象: 首先配置web.xml文件--> [html] view plaincopy <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> 然后在程序中获取: 代码: [java] view plaincopy HttpServletRequest 

springmvc在使用@ModelAttribute注解获取Request和Response会产生线程并发不安全问题(转)

springmvc在获取Request和Response有很多方式:具体请看:https://www.cnblogs.com/wade-luffy/p/8867144.html 产生线程问题的代码如下: public class BaseController { protected HttpServletRequest request; protected HttpServletResponse response; protected HttpSession session; @ModelAtt

在SpringMVC中获取request对象的几种方式

1.最简单的方式(注解法) 1 2 @Autowired private  HttpServletRequest request; 2.最麻烦的方法 a. 在web.xml中配置一个监听 <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> b.之后在程序里可以用 Http

spring MVC 中获取request

spring MVC中如何获取request 呢? 有如下方式: 方式一:在action中注入request 直接在action的参数中增加HttpServletRequest request 例如 /*** * 返回json * @param id * @param roleLevel * @param model * @param request * @param targetView * @return * @throws SecurityException * @throws NoSuc

springMVC 中几种获取request和response的方式

1.最简单方式:参数 例如: @RequestMapping("/test") @ResponseBody public void saveTest(HttpServletRequest req, HttpServletResponse resp){ } 2.加入监听器,然后在代码里面获取 HttpServletRequest req = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).ge

在SpringMVC中获取request对象

1.注解法 Java代码   @Autowired private  HttpServletRequest request; 2. 在web.xml中配置一个监听 Xml代码   <listener> <listener-class> org.springframework.web.context.request.RequestContextListener </listener-class> </listener> 之后在程序里可以用 Java代码   H

如何在SpringMVC中获取request对象

1.注解法 @Autowired private HttpServletRequest request; 2. 在web.xml中配置一个监听 HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); 3.直接在参数中引入 public String hello(HttpServletRequest request,HttpS