JAX-RS中ContainerRequestContext与HttpServletRequest的关联及创建逻辑问询
JAX-RS中ContainerRequestContext与HttpServletRequest的关联解析
刚好对这个点挺熟悉的,给你掰扯清楚这俩对象的来龙去脉,包括它们的关联、创建顺序和属性互通的底层逻辑:
一、核心关联:同一请求的两层封装
首先得明确,JAX-RS(比如Jersey、RESTeasy这些实现)是跑在Servlet容器(Tomcat、Jetty)之上的,所以这俩对象本质是同一个HTTP请求的不同抽象层级封装:
ContainerRequestContext是JAX-RS API定义的「REST专属请求上下文」,它屏蔽了Servlet底层细节,提供了REST服务常用的操作:比如获取请求URI、修改请求头、设置请求属性、直接中止请求等,是JAX-RS过滤器、拦截器的标准入参;HttpServletRequest是Servlet规范定义的「底层HTTP请求对象」,是Servlet容器直接创建的原始请求封装,包含了Servlet层面的所有细节:会话管理、请求参数、Servlet上下文、原始请求流等,也是很多开发者习惯在控制器里用的请求对象。
至于你发现的「过滤器设属性,控制器能拿到」的现象,本质是JAX-RS实现把这俩对象的属性存储做了绑定——你用ContainerRequestContext.setProperty()存的属性,最终会同步到HttpServletRequest的属性Map里,所以控制器用getAttribute()就能读到。
二、创建顺序:先底层后上层
请求从进入容器到到达你的代码,创建顺序是固定的:
- 第一步:Servlet容器创建HttpServletRequest:当HTTP请求打到Tomcat这类容器时,容器会先解析请求,生成
HttpServletRequest和HttpServletResponse实例,这是整个请求处理的起点; - 第二步:JAX-RS封装出ContainerRequestContext:请求被转发到JAX-RS的核心Servlet(比如Jersey的
ServletContainer)后,这个Servlet会基于已有的HttpServletRequest,读取它的请求方法、头信息、URI等数据,封装成符合JAX-RS规范的ContainerRequestContext对象; - 第三步:过滤器与控制器依次处理:先调用你注册的请求过滤器(传入刚创建的
ContainerRequestContext),过滤器处理完后,请求才会到达你的REST控制器,这时候你拿到的HttpServletRequest就是第一步创建的那个原始实例。
简单说:HttpServletRequest是“先出生”的底层对象,ContainerRequestContext是基于它封装出来的上层REST专属对象。
三、相互生成的底层机制
从HttpServletRequest到ContainerRequestContext
JAX-RS实现(比如Jersey)做了这些事情来完成封装:
- 读取
HttpServletRequest里的所有请求元数据(方法、URI、头、流等); - 把这些数据转换成JAX-RS规范定义的格式,生成
ContainerRequestContext实例; - 关键的一步:把
HttpServletRequest本身作为属性存入ContainerRequestContext,同时将ContainerRequestContext的属性操作(setProperty/getProperty)映射到HttpServletRequest的setAttribute/getAttribute方法上,实现属性互通。
从ContainerRequestContext获取HttpServletRequest
如果你在过滤器里需要用到Servlet层面的功能(比如操作会话),可以直接从ContainerRequestContext里拿到底层的HttpServletRequest,以Jersey为例:
// 方式1:通过标准属性键获取 HttpServletRequest servletRequest = (HttpServletRequest) requestContext.getProperty("javax.servlet.http.HttpServletRequest"); // 方式2:用Jersey提供的常量,更清晰 HttpServletRequest servletRequest = (HttpServletRequest) requestContext.getProperty(ServletProperties.ServletRequest);
反过来,如果你在控制器里想用JAX-RS的ContainerRequestContext,直接用@Context注入就行:
@GET @Path("/demo") public String demo(@Context ContainerRequestContext requestContext) { // 比如获取JAX-RS处理后的请求URI URI requestUri = requestContext.getUriInfo().getRequestUri(); return "Request URI: " + requestUri; }
内容的提问来源于stack exchange,提问作者StolenKitten
相关产品推荐
相关产品推荐

