2.12. RESTEasy Filters 和 Interceptors


Jakarta RESTful Web Services 有两个不同的拦截器概念:过滤器和拦截器。过滤器主要用于修改或处理传入和传出请求标头或响应标头。它们在请求和响应处理之前和之后执行。

2.12.1. 服务器端过滤器

在服务器端,您有两种不同类型的过滤器:Container RequestFiltersContainerResponseFiltersContainerRequestFilters 在调用 Jakarta RESTful Web Services 资源方法之前运行。ContainerResponseFilters 在调用 Jakarta RESTful Web Services 资源方法后运行。

此外,有两种类型的 ContainerRequestFilters :预匹配和后匹配。预匹配 ContainerRequestFilters@PreMatching 注释指定,并在 Jakarta RESTful Web Services 资源方法与传入 HTTP 请求匹配之前执行。匹配后 ContainerRequestFilters@PostMatching 注释指定,并在 Jakarta RESTful Web Services 资源方法与传入 HTTP 请求匹配后执行。

预匹配过滤器通常用于修改请求属性,以更改它与特定资源方法的匹配方式,如 strip .xml 并添加 Accept 标头。ContainerRequestFilters 可以通过调用 ContainerRequestContext.abortWith(Response) 来中止请求。例如,如果过滤器实施了自定义身份验证协议,它可能希望中止。

执行资源类方法后,Jakarta RESTful Web Services 运行所有 ContainerResponseFilters。通过这些过滤器,您可以在清理并发送到客户端之前修改传出响应。

示例:请求过滤器

public class RoleBasedSecurityFilter implements ContainerRequestFilter {
  protected String[] rolesAllowed;
  protected boolean denyAll;
  protected boolean permitAll;

  public RoleBasedSecurityFilter(String[] rolesAllowed, boolean denyAll, boolean permitAll) {
    this.rolesAllowed = rolesAllowed;
    this.denyAll = denyAll;
    this.permitAll = permitAll;
  }

  @Override
  public void filter(ContainerRequestContext requestContext) throws IOException  {
    if (denyAll) {
       requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build());
       return;
    }
    if (permitAll) return;
    if (rolesAllowed != null) {
       SecurityContext context = ResteasyProviderFactory.getContextData(SecurityContext.class);
       if (context != null) {
          for (String role : rolesAllowed) {
             if (context.isUserInRole(role)) return;
          }
          requestContext.abortWith(Response.status(403).entity("Access forbidden: role not allowed").build());
          return;
       }
    }
    return;
  }
}
Copy to Clipboard Toggle word wrap

示例:Response Filter

public class CacheControlFilter implements ContainerResponseFilter {
   private int maxAge;

   public CacheControlFilter(int maxAge) {
      this.maxAge = maxAge;
   }

   public void filter(ContainerRequestContext req, ContainerResponseContext res)
           throws IOException {
      if (req.getMethod().equals("GET")) {
         CacheControl cc = new CacheControl();
         cc.setMaxAge(this.maxAge);
         res.getHeaders().add("Cache-Control", cc);
      }
   }
}
Copy to Clipboard Toggle word wrap

2.12.2. 客户端过滤器

有关客户端过滤器的更多信息,请参阅本指南的 Jakarta RESTful Web Services Client API 部分。

2.12.3. RESTEasy Interceptors

2.12.3.1. 拦截 Jakarta RESTful Web Services Invocations

RESTEasy 可以拦截 Jakarta RESTful Web Services 调用,并通过类似于侦听器的对象进行路由。

虽然过滤器修改请求或响应标头,而拦截器会处理消息正文。拦截器在与其对应的读取器或写入器相同的调用堆栈中执行。ReaderInterceptors 围绕执行 MessageBodyReaders 打包.WriterInterceptors 围绕执行 MessageBodyWriters 打包。它们可用于实施特定的内容编码。它们可用于生成数字签名,或者在托管之前或之后发布或预处理 Java 对象模型。

ReaderInterceptorsWriterInterceptors 可用于服务器或客户端。它们标有 @Provider,以及 @ServerInterceptor@ClientInterceptor,以便 RESTEasy 知道是否将它们添加到拦截器列表中。

这些拦截器围绕调用 MessageBodyReader.readFrom()或 MessageBodyWriter.writeTo()进行 打包。它们可用于嵌套 输出输入 流。

示例:Interceptor

@Provider
public class BookReaderInterceptor implements ReaderInterceptor {
    @Inject private Logger log;
    @Override
    @ReaderInterceptorBinding
    public Object aroundReadFrom(ReaderInterceptorContext context) throws IOException, WebApplicationException {
        log.info("*** Intercepting call in BookReaderInterceptor.aroundReadFrom()");
        VisitList.add(this);
        Object result = context.proceed();
        log.info("*** Back from intercepting call in BookReaderInterceptor.aroundReadFrom()"); return result;
    }
}
Copy to Clipboard Toggle word wrap

拦截器和 MessageBodyReader 或 Writer 在一个大型 Java 调用堆栈中调用。ReaderInterceptorContext.proceed()WriterInterceptorContext.proceed() 被调用来进入下一个拦截器;如果没有要调用的拦截器,则调用拦截 器( ) 或 writeTo() 方法 此打包允许在对象到达 ReaderWriter 之前修改对象,然后在 continue () 返回后进行清理。

以下示例是服务器端拦截器,它为响应添加一个标头值。

@Provider
public class BookWriterInterceptor implements WriterInterceptor {
   @Inject private Logger log;

   @Override
   @WriterInterceptorBinding
   public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
      log.info("*** Intercepting call in BookWriterInterceptor.aroundWriteTo()");
      VisitList.add(this);
      context.proceed();
      log.info("*** Back from intercepting call in BookWriterInterceptor.aroundWriteTo()");
   }
}
Copy to Clipboard Toggle word wrap

2.12.3.2. 注册 Interceptor

要在应用中注册 RESTEasy Jakarta RESTful Web Services 拦截器,请将它列在 context-param 元素的 resteasy.providers 参数下的 web.xml 文件中,或者以类或对象形式在 Application. getClasses()或 Application.get Singletons() 方法中将它列出。

<context-param>
    <param-name>resteasy.providers</param-name>
    <param-value>my.app.CustomInterceptor</paramvalue>
</context-param>
Copy to Clipboard Toggle word wrap
package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

  public java.util.Set<java.lang.Class<?>> getClasses() {
    Set<Class<?>> resources = new HashSet<Class<?>>();
    resources.add(MyResource.class);
    resources.add(MyProvider.class);
    return resources;
  }
}
Copy to Clipboard Toggle word wrap
package org.jboss.resteasy.example;

import javax.ws.rs.core.Application;
import java.util.HashSet;
import java.util.Set;

public class MyApp extends Application {

    protected Set<Object> singletons = new HashSet<Object>();

    public MyApp() {
        singletons.add(new MyResource());
        singletons.add(new MyProvider());
    }

    @Override
    public Set<Object> getSingletons() {
        return singletons;
    }
}
Copy to Clipboard Toggle word wrap

2.12.4. GZIP 压缩与解压缩

RESTEasy 支持 GZIP 压缩和解压缩。为了支持 GZIP 解压缩,客户端框架或 Jakarta RESTful Web Services 服务会自动解压缩具有 gzip Content-Encoding 的消息正文,并且它可以自动将 Accept-Encoding 标头设置为 gzip,因此您不必手动设置此标头。若要支持 GZIP 压缩,如果客户端框架正在发送请求,或者服务器正在发送一个响应( Content-Encoding 标头设为 gzip ),RESTEasy 会压缩传出消息。您可以使用 @org.jboss.resteasy.annotation.GZIP 注释来设置 Content-Encoding 标头。

以下示例标记传出消息正文 进行 gzip 压缩的顺序

示例:GZIP 压缩

@Path("/")
public interface MyProxy {

   @Consumes("application/xml")
   @PUT
   public void put(@GZIP Order order);
}
Copy to Clipboard Toggle word wrap

示例:GZIP 压缩服务器响应标签

@Path("/")
public class MyService {

   @GET
   @Produces("application/xml")
   @GZIP
   public String getData() {...}
}
Copy to Clipboard Toggle word wrap

2.12.4.1. 配置 GZIP 压缩和解压缩

注意

RESTEasy 默认禁用 GZIP 压缩和解压缩,以防止对可能较大但已被攻击者压缩并发送到服务器的实体解压缩。

有三个与 GZIP 压缩和解压缩相关的拦截器:

  • org.jboss.resteasy.plugins.interceptors.GZIPDecodingInterceptor :如果 Content-Encoding 标头存在并且值为 gzipGZIPDecodingInterceptor 将安装 解压缩邮件正文的输入 流。
  • org.jboss.resteasy.plugins.interceptors.GZIPEncodingInterceptor :如果 Content-Encoding 标头存在并且值为 gzipGZIPEncodingInterceptor 会安装 压缩邮件正文的输出 流。
  • org.jboss.resteasy.plugins.interceptors.AcceptEncodingGZIPFilter :如果 Accept-Encoding 标头不存在,Accept EncodingGZIPFilter 会添加值为 gzip 的 Accept-Encoding 标头。如果 Accept-Encoding 标头存在但不包含 gzip,AcceptEncodingGZIPFilter 拦截器将附加该值 gzip

    注意

    启用 GZIP 压缩或解压缩并不依赖于 AcceptEncodingGZIPFilter 拦截器的存在性。

启用 GZIP 解压缩器可以为从压缩消息正文中提取的 GZIPDecodingInterceptor 的字节数设置上限。默认限值为 10,000,000

2.12.4.2. 服务器端 GZIP 配置

您可以通过在类路径上的 javax.ws.rs.ext.Providers 文件中包含它们的类名称来启用拦截器。已定义文件的上限使用 Web 应用上下文参数 resteasy.gzip.max.input 进行设置。如果在服务器端超过这个限制,GZIPDecodingInterceptor 将返回一个带有状态为 413 的响应 - Request Entity Too Large 以及指定上限的消息。

2.12.4.2.1. 客户端 GZIP 配置

您可以通过将 GZIP 拦截器注册到 客户端WebTarget 来启用 GZIP 拦截器。例如:

Client client = new ResteasyClientBuilder() // Activate gzip compression on client:
    .register(AcceptEncodingGZIPFilter.class)
    .register(GZIPDecodingInterceptor.class)
    .register(GZIPEncodingInterceptor.class)
    .build();
Copy to Clipboard Toggle word wrap

您可以通过创建具有特定值的 GZIPDecodingInterceptor 实例来配置定义文件的上限:

Client client = new ResteasyClientBuilder() // Activate gzip compression on client:
    .register(AcceptEncodingGZIPFilter.class)
    .register(new GZIPDecodingInterceptor(256))
    .register(GZIPEncodingInterceptor.class)
    .build();
Copy to Clipboard Toggle word wrap

如果在客户端上超过了上限,GZIPDecodingInterceptor 将 引发 ProcessingException,并显示指定上限的消息。

2.12.5. 按资源方法过滤器和 Interceptors

有时,您希望过滤器或拦截器仅针对特定资源方法运行。您可以通过两种不同的方式进行此操作:

实施 DynamicFeature Interface

DynamicFeature 界面包含回调方法,配置(ResourceInfo resourceInfo, FeatureContext context),后者会针对部署的 Jakarta RESTful Web Services 方法进行调用。ResourceInfo 参数包含有关当前部署的 Jakarta RESTful Web Services 方法的信息。FeatureContext可配置 接口的扩展。您可以使用此参数的 register() 方法绑定您要分配给此方法的过滤器和拦截器。

示例:使用动态功能接口

@Provider
public class AnimalTypeFeature implements DynamicFeature {
    @Override
    public void configure(ResourceInfo info, FeatureContext context) {
        if (info.getResourceMethod().getAnnotation(GET.class) != null)
            AnimalFilter filter = new AnimalFilter();
            context.register(filter);
        }
    }
}
Copy to Clipboard Toggle word wrap

在上例中,您使用 AnimalTypeFeature 注册的提供程序必须实施其中一个接口。本例注册了必须实现以下接口之一的供应商 AnimalFilterContainerRequestFilter、ContainerInterceptor、Reader Interceptor、WriterInterceptorFeature。在这种情况下,AnimalFilter 将应用到所有标有 GET 注解的资源方法。详情请参阅 动态功能文档

使用 @NameBinding 注解

@NameBinding 的工作方式与 Jakarta Contexts 和 Dependency Injection 拦截器非常相似。您使用 @NameBinding 标注自定义注释,然后将该自定义注释应用到过滤器和资源方法。

示例:使用 @NameBinding

@NameBinding
public @interface DoIt {}

@DoIt
public class MyFilter implements ContainerRequestFilter {...}

@Path("/root")
public class MyResource {

   @GET
   @DoIt
   public String get() {...}
}
Copy to Clipboard Toggle word wrap

详情请参阅 NameBinding 文档

2.12.6. 排序

通过对过滤器或拦截器类使用 @Priority 注释来完成排序。

2.12.7. 使用过滤器和拦截器处理异常

与过滤器或拦截器关联的例外可以在客户端或服务器端发生。在客户端,您必须处理两种类型的异常: javax.ws.rs.client.ProcessingExceptionjavax.ws.rs.client.ResponseProcessingException。如果向服务器发送请求之前 出现了错误,则将在客户端引发 javax.ws.rs.client.ProcessingException。如果处理服务器收到的响应时存在错误,则客户端将 引发 javax.ws.rs.client.ResponseProcessingException

在服务器端,由过滤器或拦截器抛出的异常处理方式与来自 Jakarta RESTful Web Services 方法的其他 异常处理方式相同,该方法尝试查找引发异常 的异常。有关如何在 Jakarta RESTful Web Services 方法中处理异常的更多详细信息,请参见 例外处理 部分。

Red Hat logoGithubredditYoutubeTwitter

学习

尝试、购买和销售

社区

关于红帽文档

通过我们的产品和服务,以及可以信赖的内容,帮助红帽用户创新并实现他们的目标。 了解我们当前的更新.

让开源更具包容性

红帽致力于替换我们的代码、文档和 Web 属性中存在问题的语言。欲了解更多详情,请参阅红帽博客.

關於紅帽

我们提供强化的解决方案,使企业能够更轻松地跨平台和环境(从核心数据中心到网络边缘)工作。

Theme

© 2026 Red Hat
返回顶部