How to exclude some url from jersey filter?
Name binding filters
Instead of excluding URIs from a global filter, you could consider using a name binding filter to select the endpoints your filter will be bound to.
Also check this answer for some examples with name binding filters.
Global filters
If you are still happy with the global filter approach, you could consider using the UriInfo
interface to get details about the requested URI. Use one of the following approaches to get an instance of UriInfo
:
Using the
@Context
annotation:@Provider public class AuthFilter implements ContainerRequestFilter { @Context private UriInfo info; @Override public void filter(ContainerRequestContext requestContext) throws IOException { ... } }
Getting it from the
ContainerRequestContext
:@Override public void filter(ContainerRequestContext requestContext) throws IOException { UriInfo info = requestContext.getUriInfo(); ... }
Once you have the UriInfo
instance, you'll have access to a bunch of methods that may be useful:
getAbsolutePath()
: Get the absolute path of the request.getBaseUri()
: Get the base URI of the application.getMatchedResources()
: Get a read-only list of the currently matched resource class instances.getMatchedURIs()
: Get a read-only list of URIs for matched resources.getPath()
: Get the path of the current request relative to the base URI as a string.getPathSegments()
: Get the path of the current request relative to the base URI as a list ofPathSegment
.getRequestUri()
: Get the absolute request URI including any query parameters.relativize(URI)
: Relativize a URI with respect to the current request URI.resolve(URI)
: Resolve a relative URI with respect to the base URI of the application.
For more details, check the UriInfo
documentation.
If the requested URI does not match the URIs you want to apply the filter to, simply use a return
instruction:
@Override
public void filter(ContainerRequestContext requestContext) throws IOException {
UriInfo info = requestContext.getUriInfo();
if (!info.getPath().contains("secured")) {
return;
}
}
Dynamic binding
Another approach is dynamic binding. It allows you to assign filters and interceptors to the resource methods in a dynamic manner. Name binding, mentioned above, uses a static approach and changes to binding require source code change and recompilation. With dynamic binding you can implement code which defines bindings during the application initialization time.
The following example extracted from the Jersey documentation shows how to implement dynamic binding:
@Path("helloworld")
public class HelloWorldResource {
@GET
@Produces("text/plain")
public String getHello() {
return "Hello World!";
}
@GET
@Path("too-much-data")
public String getVeryLongString() {
String str = ... // very long string
return str;
}
}
// This dynamic binding provider registers GZIPWriterInterceptor
// only for HelloWorldResource and methods that contain
// "VeryLongString" in their name. It will be executed during
// application initialization phase.
public class CompressionDynamicBinding implements DynamicFeature {
@Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if (HelloWorldResource.class.equals(resourceInfo.getResourceClass())
&& resourceInfo.getResourceMethod().getName().contains("VeryLongString")) {
context.register(GZIPWriterInterceptor.class);
}
}
}
The binding is done using the provider which implements the DynamicFeature
interface. The interface defines one configure
method with two arguments, ResourceInfo
and FeatureContext
.
ResourceInfo
contains information about the resource and method to which the binding can be done. The configure method will be executed once for each resource method that is defined in the application. In the example above the provider will be executed twice, once for the getHello()
method and once for getVeryLongString()
(once the resourceInfo
will contain information about getHello()
method and once it will point to getVeryLongString()
).
If a dynamic binding provider wants to register any provider for the actual resource method it will do that using provided FeatureContext
which extends JAX-RS Configurable API. All methods for registration of filter or interceptor classes or instances can be used. Such dynamically registered filters or interceptors will be bound only to the actual resource method. In the example above the GZIPWriterInterceptor
will be bound only to the method getVeryLongString()
which will cause that data will be compressed only for this method and not for the method getHello()
.
Note that filters and interceptors registered using dynamic binding are only additional filters run for the resource method. If there are any name bound providers or global providers they will still be executed.
For more details, check the Jersey documentation about filters and interceptors.
Using @NameBinding may be the most elegant approach, but if you just want to exclude a single resource and apply the filter on all others you have to remember putting the binding annotation on all resources. In this case you can use ContainerRequestContext.getUriInfo().getMatchedResources()
to check whether the target resource has been matched. This is better than hard-coding a path that might change.
The example below will apply the filter logic on all resources but StatusResource:
public class CorsContainerRequestFilter implements ContainerRequestFilter {
@Override
public void filter(ContainerRequestContext req) {
if (!matchesStatusResource(req)) {
// filter logic
}
}
private boolean matchesStatusResource(ContainerRequestContext req) {
List<Object> matchedResources = req.getUriInfo().getMatchedResources();
for (Object matchedResource : matchedResources) {
if (matchedResource instanceof StatusResource) {
return true;
}
}
return false;
}
}
As mentioned by others Dynamic bindings can be used instead but it is quite ugly as it is not obvious that the filter wouldn't be applied to all resources.