12.3.1Built-In Client Interceptors

 

This page describes some client interceptors that are shipped with HAPI FHIR out of the box. Of course, you are also welcome to create your own.

12.3.2Logging: Logging Interceptor

 

The LoggingInterceptor logs details about each request and/or response that is performed using the client. All logging is performed using SLF4j.

LoggingInterceptor is highly configurable in terms of its output. It can be configured to log simple details about requests, or detailed output including payload bodies and header contents. The following example shows how to enable LoggingInterceptor.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// Create a logging interceptor
LoggingInterceptor loggingInterceptor = new LoggingInterceptor();

// Optionally you may configure the interceptor (by default only
// summary info is logged)
loggingInterceptor.setLogRequestSummary(true);
loggingInterceptor.setLogRequestBody(true);

// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(loggingInterceptor);

IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(loggingInterceptor);

12.3.3Security: HTTP Basic Authorization

 

The BasicAuthInterceptor adds an Authorization header containing an HTTP Basic Auth (username+password) token in every outgoing request.

The following example shows how to configure your client to use a specific username and password in every request.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// Create an HTTP basic auth interceptor
String username = "foobar";
String password = "boobear";
IClientInterceptor authInterceptor = new BasicAuthInterceptor(username, password);

// If you're using an annotation client, use this style to
// register it
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);

// If you're using a generic client, use this instead
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
genericClient.registerInterceptor(authInterceptor);

12.3.4Security: HTTP Bearer Token Authorization

 

The BearerTokenAuthInterceptor can be used to add an Authorization header containing a bearer token (typically used for OIDC/OAuth2/SMART security flows) to every outgoing request.

The following example shows how to configure your client to inject a bearer token authorization header into every request.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// In reality the token would have come from an authorization server
String token = "3w03fj.r3r3t";

BearerTokenAuthInterceptor authInterceptor = new BearerTokenAuthInterceptor(token);

// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);

IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(authInterceptor);

12.3.5Misc: Add Headers to Request

 

The AdditionlRequestHeadersInterceptor can be used to add arbitrary headers to each request created by the client.

The following example shows how to configure your client to inject a bearer token authorization header into every request.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// Create a client
IGenericClient client = ctx.newRestfulGenericClient("http://localhost:9999/fhir");

// Register an additional headers interceptor and add one header to it
AdditionalRequestHeadersInterceptor interceptor = new AdditionalRequestHeadersInterceptor();
interceptor.addHeaderValue("X-Message", "Help I'm a Bug");
client.registerInterceptor(interceptor);

IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
client.registerInterceptor(interceptor);

Note that headers can also be added to individual Generic Client invocations inline. The example below will produce the same additional request header as the example above, although it applies only to the one request.

Patient p = client.read()
      .resource(Patient.class)
      .withId(123L)
      .withAdditionalHeader("X-Message", "Help I'm a Bug")
      .execute();

12.3.6Misc: Add Cookies to Request

 

The CookieInterceptor can be used to add an HTTP Cookie header to each request created by the client.

The following example shows how to configure your client to inject a bearer token authorization header into every request.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// Create a cookie interceptor. This cookie will have the name "mycookie" and
// the value "Chips Ahoy"
CookieInterceptor interceptor = new CookieInterceptor("mycookie=Chips Ahoy");

// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);

IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");
annotationClient.registerInterceptor(interceptor);

12.3.7Multitenancy: Add tenant ID to path

 

When communicating with a server that supports URL Base Multitenancy, an extra element needs to be added to the request path. This can be done by simply appending the path to the base URL supplied to the client, but it can also be dynamically appended using this interceptor.

FhirContext ctx = FhirContext.forR4();

// Create the client
IGenericClient genericClient = ctx.newRestfulGenericClient("http://localhost:9999/fhir");

// Register the interceptor
UrlTenantSelectionInterceptor tenantSelection = new UrlTenantSelectionInterceptor();
genericClient.registerInterceptor(tenantSelection);

// Read from tenant A
tenantSelection.setTenantId("TENANT-A");
Patient patientA =
      genericClient.read().resource(Patient.class).withId("123").execute();

// Read from tenant B
tenantSelection.setTenantId("TENANT-B");
Patient patientB =
      genericClient.read().resource(Patient.class).withId("456").execute();

12.3.8Performance: GZip Outgoing Request Bodies

 

The GZipContentInterceptor compresses outgoing contents. With this interceptor, if the client is transmitting resources to the server (e.g. for a create, update, transaction, etc.) the content will be GZipped before transmission to the server.

The following example shows how to enable the GZipContentInterceptor.

// Create a context and get the client factory so it can be configured
FhirContext ctx = FhirContext.forR4();
IRestfulClientFactory clientFactory = ctx.getRestfulClientFactory();

// Register the interceptor with your client (either style)
IPatientClient annotationClient = ctx.newRestfulClient(IPatientClient.class, "http://localhost:9999/fhir");
annotationClient.registerInterceptor(new GZipContentInterceptor());

12.3.9Capture: Programmatically Capturing Request/Response Details

 

The CapturingInterceptor can be used to capture the details of the last request that was sent by the client, as well as the corresponding response that was received.

A separate but related interceptor called ThreadLocalCapturingInterceptor also captures request/response pairs but stores these in a Java ThreadLocal so it is suitable for use in multithreaded environments.