Groups keyboard shortcuts have been updated
Dismiss
See shortcuts

IGenericClient and Response-Headers and Empty-Response-Body

7 views
Skip to first unread message

granada Coder

unread,
Apr 25, 2025, 4:00:16 PMApr 25
to HAPI FHIR



Getting a header (reponse) value with IGenericClient.

REQUEST:

request headers

Prefer: respond-async
Accept: application/fhir+json


POSTMAN RESPONSE:

202 Accepted
No-Response-Body
Header of "Content-Location" with a value like (of (some guid)):



....

I am coding as to the example you (James) provided earlier this week.


And instead of Patient $everything with a "1",
I am using
Group $export with "EXherd1"

(I found "EXherd1" using http://hapi.fhir.org/baseR4/Group/ )


Quesiton 1.

How I can read the "Content-Location" header.


Question 2.

I am trying to figure out the "graceful" way to handle .. if a Group Export request.. with the return
with no-response-body
OR
an operation-outcome.


....

I am currently getting :

NonFhirResponseException (202) and message of "Response contains no Content-Type"

(of course being generated by NonFhirResponseException extends BaseServerResponseException )


granada Coder

unread,
Apr 29, 2025, 4:27:57 PMApr 29
to HAPI FHIR
Ok. 

Just to follow up. 



3.1.6.3.0.2 Response - Success 
  • HTTP Status Code of 202 Accepted
  • Optionally a FHIR OperationOutcome in the body

The OperationOutcome is "optional".  Thus makes it a little tricker.

I ended up writing my own Interceptor. (based off an existing LoggingInterceptor.

And using the pieces of information it captured...to create my own OptionalOperationOutcomeWrapper

The inceptor is wired to the IGenericClient before making the .operation() call.


Then when it fires, it collects pieces of the Response.  And then I use those pieces to create my own OptionalOperationOutcomeWrapper

it is not perfect.... but I was able to solve the "optional" OperationOutcome".


/**
* This class is a wrapper for an optional OperationOutcome and a map of response headers.
* Some operations (especially the async-ones) stipulate the OperationOutcome is optional.
* See <a href="https://hl7.org/fhir/R4/async.html#3.1.6.2.0.3">Async Response Success</a>
*/
public final class OptionalOperationOutcomeWrapper extends DomainResource {

private final transient Optional<OperationOutcome> operationOutcome;

private final transient Optional<IdDt> locationIdDt;

private final Map<String, List<String>> responseHeaderValues;

public OptionalOperationOutcomeWrapper() {
this(Optional.empty(), Optional.empty(), Collections.emptyMap());
}

public OptionalOperationOutcomeWrapper(final Map<String, List<String>> responseHeaderValues) {
this(Optional.empty(), Optional.empty(), responseHeaderValues);
}

public OptionalOperationOutcomeWrapper(final Optional<OperationOutcome> operationOutcome, final Optional<IdDt> locationIdDt, final Map<String, List<String>> responseHeaderValues) {
super();
this.operationOutcome = operationOutcome;
this.locationIdDt = locationIdDt;
this.responseHeaderValues = responseHeaderValues;
}

public Optional<OperationOutcome> getOperationOutcome() {
return operationOutcome;
}

public Optional<IdDt> getLocationIdDt() {
return locationIdDt;
}

public Map<String, List<String>> getResponseHeaderValues() {
return responseHeaderValues;
}

@Override
public DomainResource copy() {
OptionalOperationOutcomeWrapper retVal = new OptionalOperationOutcomeWrapper(this.operationOutcome, this.locationIdDt, this.responseHeaderValues);
super.copyValues(retVal);
return retVal;
}

@Override
public ResourceType getResourceType() {
/* this return of null is intentional */
return null;
}
}




import ca.uhn.fhir.interceptor.api.Hook;
import ca.uhn.fhir.interceptor.api.Interceptor;
import ca.uhn.fhir.interceptor.api.Pointcut;
import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.client.api.IClientInterceptor;
import ca.uhn.fhir.rest.client.api.IHttpRequest;
import ca.uhn.fhir.rest.client.api.IHttpResponse;
import ca.uhn.fhir.rest.client.interceptor.InterceptorOrders;
import ca.uhn.fhir.util.StopWatch;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

/**
* loosely based on ca.uhn.fhir.rest.client.interceptor.LoggingInterceptor
* Add an interceptor so extra information can be captured.
*/
@Interceptor
public final class HapiFhirClientResponseInformationCaptureInterceptor implements IClientInterceptor {

private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory.getLogger(HapiFhirClientResponseInformationCaptureInterceptor.class);

private boolean isPerformCaptureRequestBody = false;

private boolean isPerformCaptureRequestHeaders = false;

private boolean isPerformCaptureResponseHttpStatus = false;

private boolean isPerformCaptureResponseBody = false;

private boolean isPerformCaptureResponseHeaders = false;

private boolean isPerformCaptureResponseSpecialLocationHeader = false;

private boolean isPerformCaptureStopWatch = false;

private Optional<StopWatch> requestStopWatch = Optional.empty();

private Map<String, List<String>> requestHeadersMap = new HashMap<>();

private Optional<String> requestBody = Optional.empty();

private Optional<Integer> responseStatus = Optional.empty();

private Optional<String> responseStatusInfo = Optional.empty();

private Map<String, List<String>> responseHeadersMap = new HashMap<>();

private byte[] responseBody = null;

private Optional<IdDt> locationIdDt = Optional.empty();

private Optional<String> locationValue = Optional.empty();

/**
* default constructor.
*/
public HapiFhirClientResponseInformationCaptureInterceptor() {
super();
}

/**
* Constructor for client logging interceptor.
*
* @param captureEverything If set to true, all capturing is enabled
*/
public HapiFhirClientResponseInformationCaptureInterceptor(boolean captureEverything) {
if (captureEverything) {
setIsPerformCaptureRequestBody(true);
setIsPerformCaptureRequestHeaders(true);

setIsPerformCaptureResponseBody(true);
setIsPerformCaptureResponseHeaders(true);
setIsPerformCaptureResponseHttpStatus(true);

setIsPerformCaptureSpecialLocationHeader(true);
setIsPerformCaptureStopWatch(true);
}
}

@Override
@Hook(value = Pointcut.CLIENT_REQUEST, order = InterceptorOrders.LOGGING_INTERCEPTOR_RESPONSE)
public void interceptRequest(IHttpRequest theRequest) {

if (isPerformCaptureRequestHeaders && null != theRequest && null != theRequest.getAllHeaders()) {
requestHeadersMap = theRequest.getAllHeaders();
}

if (isPerformCaptureRequestBody) {
try {
if (null != theRequest && null != theRequest.getRequestBodyFromStream()) {
String content = theRequest.getRequestBodyFromStream();
if (content != null) {
this.requestBody = Optional.of(content);
}
}
} catch (IllegalStateException | IOException e) {
LOGGER.warn(
"Failed to replay request contents (during logging attempt, actual FHIR call did not fail)", e);
}
}
}

@Override
@Hook(value = Pointcut.CLIENT_RESPONSE, order = InterceptorOrders.LOGGING_INTERCEPTOR_REQUEST)
public void interceptResponse(IHttpResponse theResponse) throws IOException {

if (isPerformCaptureResponseHttpStatus) {
this.responseStatus = Optional.of(theResponse.getStatus());
this.responseStatusInfo = Optional.of(theResponse.getStatusInfo());
}

if (isPerformCaptureResponseSpecialLocationHeader) {
captureLocationHeaderInformation(theResponse);
}

if (isPerformCaptureStopWatch && null != theResponse.getRequestStopWatch()) {
this.requestStopWatch = Optional.of(theResponse.getRequestStopWatch());
}

if (isPerformCaptureResponseHeaders && null != theResponse && null != theResponse.getAllHeaders()) {
responseHeadersMap = theResponse.getAllHeaders();
}

if (isPerformCaptureResponseBody && null != theResponse) {
theResponse.bufferEntity();
try (InputStream respEntity = theResponse.readEntity()) {
if (respEntity != null) {
final byte[] bytes;
bytes = IOUtils.toByteArray(respEntity);
this.responseBody = bytes;
}
}
}
}

private void captureLocationHeaderInformation(IHttpResponse theResponse) {
/*
* Add response location
*/
List<String> locationHeaders = theResponse.getHeaders(Constants.HEADER_LOCATION);
if (locationHeaders == null || locationHeaders.isEmpty()) {
locationHeaders = theResponse.getHeaders(Constants.HEADER_CONTENT_LOCATION);
}
if (locationHeaders != null && !locationHeaders.isEmpty()) {
String locationValueIntermediate = locationHeaders.get(0);
IdDt locationValueId = new IdDt(locationValueIntermediate);
this.locationIdDt = Optional.of(locationValueId);
if (locationValueId.hasBaseUrl() && locationValueId.hasIdPart()) {
locationValueIntermediate = locationValueId.toUnqualified().getValue();
}

if (!StringUtils.isBlank(locationValueIntermediate)) {
this.locationValue = Optional.of(locationValueIntermediate);
}
}
}

/**
* Should a summary (one line) for each request be logged, containing the URL and other information.
*/
public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureRequestBody(boolean isCapturedEnabled) {
isPerformCaptureRequestBody = isCapturedEnabled;
return this;
}

/**
* Should headers for each request be logged, containing the URL and other information.
*/
public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureRequestHeaders(boolean isCapturedEnabled) {
isPerformCaptureRequestHeaders = isCapturedEnabled;
return this;
}

/**
* Should a summary (one line) for each request be logged, containing the URL and other information.
*/
public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureResponseBody(boolean isCapturedEnabled) {
isPerformCaptureResponseBody = isCapturedEnabled;
return this;
}

/**
* Should headers for each request be logged, containing the URL and other information.
*/
public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureResponseHeaders(boolean isCapturedEnabled) {
isPerformCaptureResponseHeaders = isCapturedEnabled;
return this;
}

/**
* Should a summary (one line) for each request be logged, containing the URL and other information.
*/
public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureSpecialLocationHeader(boolean isCapturedEnabled) {
isPerformCaptureResponseSpecialLocationHeader = isCapturedEnabled;
return this;
}

public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureResponseHttpStatus(boolean isCapturedEnabled) {
isPerformCaptureResponseHttpStatus = isCapturedEnabled;
return this;
}

public HapiFhirClientResponseInformationCaptureInterceptor setIsPerformCaptureStopWatch(boolean isCapturedEnabled) {
isPerformCaptureStopWatch = isCapturedEnabled;
return this;
}

public Optional<StopWatch> getRequestStopWatch() {
return requestStopWatch;
}

public Optional<Integer> getResponseStatus() {
return responseStatus;
}

public Optional<String> getResponseStatusInfo() {
return responseStatusInfo;
}

public Optional<String> getRequestBody() {
return requestBody;
}

public Map<String, List<String>> getRequestHeadersMap() {
return requestHeadersMap;
}

public Map<String, List<String>> getResponseHeadersMap() {
return responseHeadersMap;
}

public byte[] getResponseBody() {
return responseBody;
}

public Optional<IdDt> getLocationIdDt() {
return locationIdDt;
}

public Optional<String> getLocationValue() {
return locationValue;
}
}
Reply all
Reply to author
Forward
0 new messages