-
Notifications
You must be signed in to change notification settings - Fork 1.9k
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 support for multiple ResponseInterceptors #1829
Changes from 4 commits
336d46a
37677cb
1901cf7
52f55b0
dd4f2f2
ffd6b14
95fed1b
754af77
34bfeb6
29ef109
a74dbd7
2852b6a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,7 +35,7 @@ public abstract class BaseBuilder<B extends BaseBuilder<B>> { | |
|
||
protected final List<RequestInterceptor> requestInterceptors = | ||
new ArrayList<>(); | ||
protected ResponseInterceptor responseInterceptor = ResponseInterceptor.DEFAULT; | ||
protected final List<ResponseInterceptor> responseInterceptors = new ArrayList<>(); | ||
protected Logger.Level logLevel = Logger.Level.NONE; | ||
protected Contract contract = new Contract.Default(); | ||
protected Retryer retryer = new Retryer.Default(); | ||
|
@@ -197,11 +197,23 @@ public B requestInterceptors(Iterable<RequestInterceptor> requestInterceptors) { | |
return thisB; | ||
} | ||
|
||
/** | ||
* Sets the full set of request interceptors for the builder, overwriting any previous | ||
* interceptors. | ||
*/ | ||
public B responseInterceptors(Iterable<ResponseInterceptor> responseInterceptors) { | ||
this.responseInterceptors.clear(); | ||
for (ResponseInterceptor responseInterceptor : responseInterceptors) { | ||
this.responseInterceptors.add(responseInterceptor); | ||
} | ||
return thisB; | ||
} | ||
|
||
/** | ||
* Adds a single response interceptor to the builder. | ||
*/ | ||
public B responseInterceptor(ResponseInterceptor responseInterceptor) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about add Deprecated annotation? Without looking at the implementation it seems difficult to infer the behavior. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But it's not deprecated, it just changed from you can have only one, to you can have as many as you want, like requestInterceptor |
||
this.responseInterceptor = responseInterceptor; | ||
this.responseInterceptors.add(responseInterceptor); | ||
return thisB; | ||
} | ||
|
||
|
@@ -269,5 +281,19 @@ List<Field> getFieldsToEnrich() { | |
.collect(Collectors.toList()); | ||
} | ||
|
||
protected ResponseInterceptor.Chain executionChain() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can we consider a more descriptive name? How about |
||
ResponseInterceptor.Chain endOfChain = | ||
ResponseInterceptor.Chain.DEFAULT; | ||
|
||
ResponseInterceptor.Chain executionChain = this.responseInterceptors.stream() | ||
.reduce(ResponseInterceptor::andThen) | ||
.map(interceptor -> interceptor.apply(endOfChain)) | ||
.orElse(endOfChain); | ||
|
||
return (ResponseInterceptor.Chain) Capability.enrich(executionChain, | ||
ResponseInterceptor.Chain.class, capabilities); | ||
|
||
} | ||
|
||
|
||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,26 +13,101 @@ | |
*/ | ||
package feign; | ||
|
||
import static feign.FeignException.errorReading; | ||
import feign.codec.DecodeException; | ||
import feign.codec.Decoder; | ||
import java.io.IOException; | ||
import java.lang.reflect.Type; | ||
import java.util.function.Function; | ||
|
||
/** | ||
* Zero or One {@code ResponseInterceptor} may be configured for purposes such as verify or modify | ||
* headers of response, verify the business status of decoded object. Once interceptors are applied, | ||
* {@link ResponseInterceptor#aroundDecode(Response, Function)} is called around decode method | ||
* called | ||
* Interceptor for purposes such as verify or modify headers of response, verify the business status | ||
* of decoded object. Once interceptors are applied, | ||
* {@link ResponseInterceptor#intercept(Response, Function)} is called around decode method called | ||
*/ | ||
public interface ResponseInterceptor { | ||
|
||
ResponseInterceptor DEFAULT = InvocationContext::proceed; | ||
|
||
/** | ||
* Called for response around decode, must either manually invoke | ||
* {@link InvocationContext#proceed} or manually create a new response object | ||
* Called for response around decode, must either manually invoke {@link Chain#next(Context)} or | ||
* manually create a new response object | ||
* | ||
* @param invocationContext information surrounding the response been decoded | ||
* @return decoded response | ||
*/ | ||
Object aroundDecode(InvocationContext invocationContext) throws IOException; | ||
Object intercept(Context context, Chain chain) throws IOException; | ||
|
||
/** | ||
* Return a new {@link ResponseInterceptor} that invokes the current interceptor first and then | ||
* the one that is passed in. | ||
* | ||
* @param nextInterceptor the interceptor to delegate to after the current | ||
* @return a new interceptor that chains the two | ||
*/ | ||
default ResponseInterceptor andThen(ResponseInterceptor nextInterceptor) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This feels an awful lot like |
||
return (ic, chain) -> intercept(ic, | ||
nextContext -> nextInterceptor.intercept(nextContext, chain)); | ||
} | ||
|
||
/** | ||
* Contract for delegation to the rest of the chain. | ||
*/ | ||
public interface Chain { | ||
Chain DEFAULT = ic -> { | ||
try { | ||
return ic.decoder().decode(ic.response(), ic.returnType()); | ||
} catch (final FeignException e) { | ||
throw e; | ||
} catch (final RuntimeException e) { | ||
throw new DecodeException(ic.response().status(), e.getMessage(), | ||
ic.response().request(), e); | ||
} catch (IOException e) { | ||
throw errorReading(ic.response().request(), ic.response(), e); | ||
} | ||
}; | ||
|
||
/** | ||
* Delegate to the rest of the chain to execute the request. | ||
* | ||
* @param context the request to execute the {@link Chain} . | ||
* @return the response | ||
*/ | ||
Object next(Context context) throws IOException; | ||
} | ||
|
||
/** | ||
* Apply this interceptor to the given {@code Chain} resulting in an intercepted chain. | ||
* | ||
* @param chain the chain to add interception around | ||
* @return a new chain instance | ||
*/ | ||
default Chain apply(Chain chain) { | ||
return request -> intercept(request, chain); | ||
} | ||
|
||
public class Context { | ||
|
||
private final Decoder decoder; | ||
private final Type returnType; | ||
private final Response response; | ||
|
||
Context(Decoder decoder, Type returnType, Response response) { | ||
this.decoder = decoder; | ||
this.returnType = returnType; | ||
this.response = response; | ||
} | ||
|
||
public Decoder decoder() { | ||
return decoder; | ||
} | ||
|
||
public Type returnType() { | ||
return returnType; | ||
} | ||
|
||
public Response response() { | ||
return response; | ||
} | ||
|
||
} | ||
Comment on lines
+87
to
+111
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
|
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method clears the current list of response interceptors and replaces it with a new set. This could potentially remove interceptors that were previously added and are still needed. Consider providing a method to append to the list of interceptors instead of replacing them entirely.