Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ResponseInterceptor support #1610

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 16 additions & 2 deletions core/src/main/java/feign/Feign.java
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ public static class Builder {

private final List<RequestInterceptor> requestInterceptors =
new ArrayList<RequestInterceptor>();
private final List<ResponseInterceptor> responseInterceptors =
new ArrayList<ResponseInterceptor>();
private Logger.Level logLevel = Logger.Level.NONE;
private Contract contract = new Contract.Default();
private Client client = new Client.Default(null, null);
Expand Down Expand Up @@ -227,6 +229,14 @@ public Builder requestInterceptor(RequestInterceptor requestInterceptor) {
return this;
}

/**
* Adds a single response interceptor to the builder.
*/
public Builder responseInterceptor(ResponseInterceptor responseInterceptor) {
this.responseInterceptors.add(responseInterceptor);
return this;
}

/**
* Sets the full set of request interceptors for the builder, overwriting any previous
* interceptors.
Expand Down Expand Up @@ -297,6 +307,9 @@ public Feign build() {
List<RequestInterceptor> requestInterceptors = this.requestInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
List<ResponseInterceptor> responseInterceptors = this.responseInterceptors.stream()
.map(ri -> Capability.enrich(ri, capabilities))
.collect(Collectors.toList());
Logger logger = Capability.enrich(this.logger, capabilities);
Contract contract = Capability.enrich(this.contract, capabilities);
Options options = Capability.enrich(this.options, capabilities);
Expand All @@ -307,8 +320,9 @@ public Feign build() {
QueryMapEncoder queryMapEncoder = Capability.enrich(this.queryMapEncoder, capabilities);

SynchronousMethodHandler.Factory synchronousMethodHandlerFactory =
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors, logger,
logLevel, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding);
new SynchronousMethodHandler.Factory(client, retryer, requestInterceptors,
responseInterceptors,logger, logLevel, dismiss404, closeAfterDecode,
propagationPolicy, forceDecoding);
ParseHandlersByName handlersByName =
new ParseHandlersByName(contract, options, encoder, decoder, queryMapEncoder,
errorDecoder, synchronousMethodHandlerFactory);
Expand Down
29 changes: 29 additions & 0 deletions core/src/main/java/feign/ResponseInterceptor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package feign;
feiyanke marked this conversation as resolved.
Show resolved Hide resolved

/**
* Zero or more {@code ResponseInterceptor} may be configured for purposes
* such as verify or modify headers of response, verify the business status of decoded object.
* No guarantees are given with regards to the order that interceptors are applied.
* Once interceptors are applied, {@link ResponseInterceptor#beforeDecode(Response)} is called
* before decode method called, {@link ResponseInterceptor#afterDecode(Object)} is called
* after decode method called.
*/
public interface ResponseInterceptor {

/**
* Called for response before decode, add data on the supplied {@link Response} or doing
* customized logic
*
* @param response
* @return
*/
void beforeDecode(Response response);

/**
* Called for response after decode, add data to decoded object or doing customized logic
*
* @param response
* @return
*/
void afterDecode(Object response);
}
34 changes: 27 additions & 7 deletions core/src/main/java/feign/SynchronousMethodHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ final class SynchronousMethodHandler implements MethodHandler {
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final List<ResponseInterceptor> responseInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final RequestTemplate.Factory buildTemplateFromArgs;
Expand All @@ -48,8 +49,8 @@ final class SynchronousMethodHandler implements MethodHandler {


private SynchronousMethodHandler(Target<?> target, Client client, Retryer retryer,
List<RequestInterceptor> requestInterceptors, Logger logger,
Logger.Level logLevel, MethodMetadata metadata,
List<RequestInterceptor> requestInterceptors, List<ResponseInterceptor> responseInterceptors,
Logger logger, Logger.Level logLevel, MethodMetadata metadata,
RequestTemplate.Factory buildTemplateFromArgs, Options options,
Decoder decoder, ErrorDecoder errorDecoder, boolean dismiss404,
boolean closeAfterDecode, ExceptionPropagationPolicy propagationPolicy,
Expand All @@ -60,6 +61,8 @@ private SynchronousMethodHandler(Target<?> target, Client client, Retryer retrye
this.retryer = checkNotNull(retryer, "retryer for %s", target);
this.requestInterceptors =
checkNotNull(requestInterceptors, "requestInterceptors for %s", target);
this.responseInterceptors =
checkNotNull(responseInterceptors, "responseInterceptors for %s", target);
this.logger = checkNotNull(logger, "logger for %s", target);
this.logLevel = checkNotNull(logLevel, "logLevel for %s", target);
this.metadata = checkNotNull(metadata, "metadata for %s", target);
Expand Down Expand Up @@ -130,9 +133,19 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa
}
long elapsedTime = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - start);

for (ResponseInterceptor interceptor: responseInterceptors) {
interceptor.beforeDecode(response);
}

if (decoder != null) {
Object object = decoder.decode(response, metadata.returnType());

for (ResponseInterceptor interceptor: responseInterceptors) {
interceptor.afterDecode(object);
}

if (decoder != null)
return decoder.decode(response, metadata.returnType());
return object;
}

CompletableFuture<Object> resultFuture = new CompletableFuture<>();
asyncResponseHandler.handleResponse(resultFuture, metadata.configKey(), response,
Expand All @@ -143,7 +156,11 @@ Object executeAndDecode(RequestTemplate template, Options options) throws Throwa
if (!resultFuture.isDone())
throw new IllegalStateException("Response handling not done");

return resultFuture.join();
Object object = resultFuture.join();
for (ResponseInterceptor interceptor: responseInterceptors) {
interceptor.afterDecode(object);
}
return object;
} catch (CompletionException e) {
Throwable cause = e.getCause();
if (cause != null)
Expand Down Expand Up @@ -179,6 +196,7 @@ static class Factory {
private final Client client;
private final Retryer retryer;
private final List<RequestInterceptor> requestInterceptors;
private final List<ResponseInterceptor> responseInterceptors;
private final Logger logger;
private final Logger.Level logLevel;
private final boolean dismiss404;
Expand All @@ -187,11 +205,13 @@ static class Factory {
private final boolean forceDecoding;

Factory(Client client, Retryer retryer, List<RequestInterceptor> requestInterceptors,
List<ResponseInterceptor> responseInterceptors,
Logger logger, Logger.Level logLevel, boolean dismiss404, boolean closeAfterDecode,
ExceptionPropagationPolicy propagationPolicy, boolean forceDecoding) {
this.client = checkNotNull(client, "client");
this.retryer = checkNotNull(retryer, "retryer");
this.requestInterceptors = checkNotNull(requestInterceptors, "requestInterceptors");
this.responseInterceptors = checkNotNull(responseInterceptors, "responseInterceptors");
this.logger = checkNotNull(logger, "logger");
this.logLevel = checkNotNull(logLevel, "logLevel");
this.dismiss404 = dismiss404;
Expand All @@ -206,8 +226,8 @@ public MethodHandler create(Target<?> target,
Options options,
Decoder decoder,
ErrorDecoder errorDecoder) {
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors, logger,
logLevel, md, buildTemplateFromArgs, options, decoder,
return new SynchronousMethodHandler(target, client, retryer, requestInterceptors,
responseInterceptors, logger, logLevel, md, buildTemplateFromArgs, options, decoder,
errorDecoder, dismiss404, closeAfterDecode, propagationPolicy, forceDecoding);
}
}
Expand Down