-
Notifications
You must be signed in to change notification settings - Fork 16
Intercepting Requests
Since the prime objective is to provide a higher level of abstraction over tedious network programming, the underlying Apache HC code is rarely exposed. Nonetheless, it's wise to acknowledge the fact that sometimes you may want to reach under the hood. This can be achieved using an Interceptor
.
Every invocation goes through a processor chain which builds the request before execution. An Interceptor
will allow you to hook into the very end of the request processor chain and manipulate the request just prior to execution.
URI |
→ | Headers |
→ | Query |
→ | Form |
→ | Entity |
→ | Intercept |
→ | EXECUTE |
---|
The request processor chain depicting the sequence of processors and point of interception before request execution.
####1. Creating Interceptors
Every interceptor is an implementation of Interceptor
where the intercept(...)
method is expected to process the request before execution. The InvocationContext
which is available within intercept(...)
supplies all the information about the current invocation.
Note that all interceptors exist as singletons and are expected to be thread-safe. If an incurred state affects thread-safety, ensure that proper thread-local management is performed.
public class DebugInterceptor implements Interceptor {
@Override
public void intercept(InvocationContext context, HttpRequestBase request) {
Log.d("MyApp-v1", request.getRequestLine().toString());
}
}
####2. Attaching Interceptors
Multiple interceptors can be attached to either a single request or an entire endpoint. Use @Intercept
with the Class
of the Interceptor
.
@Endpoint("http://example.com")
@Intercept(DebugInterceptor.class)
public interface ExampleEndpoint {
@GET("/document")
@Intercept(HeaderInterceptor.class)
String getDocument();
@PUT("/document")
@Intercept({GrammarInterceptor.class,
PlagiarismInterceptor.class})
void putDocument(@Entity String document);
}
####3. Interceptors As Arguments
Interceptor instances can also be passed as arguments to a request method. Simply define one or multiple Interceptor
types as parameters and pass an instance for each argument.
@Endpoint("http://example.com")
public interface ExampleEndpoint {
@PUT("/content")
void putContent(Interceptor redactor);
}
...
final StringBuilder content = ...
final List<String> redactedTerms = ...
endpoint.putContent(new Interceptor() {
@Override
public void intercept(InvocationContext context, HttpRequestBase request) {
for (String term : redactedTerms) {
...
}
StringEntity entity = new StringEntity(content.toString());
((HttpEntityEnclosingRequestBase)request).setEntity(entity);
}
});
####4. Beyond Interception
Interceptors are quite versatile and may prove to be invaluable when used with a little imagination. The code snippet below demonstrates the use of interceptors in metadata processing with user-defined annotations.
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Auth {}
public class AuthInterceptor implements Interceptor {
private final AuthManager authManager;
public AuthInterceptor() {
this.authManager = new OAuthService();
}
@Override
public void intercept(InvocationContext context, HttpRequestBase request) {
if(context.getRequest().isAnnotationPresent(Auth.class)) {
CharSequence oAuthHeader = authManager.getOAuthHeader();
request.addHeader("Authorization", oAuthHeader);
}
}
}
@Deserialize(JSON)
@Intercept(AuthInterceptor.class)
@Endpoint("https://api.github.com")
public interface GitHubEndpoint {
@Auth @GET("/user/repos")
List<Repo> getRepos();
@GET("/users/{user}/gists")
List<Gist> getGists(@PathParam("user") String user);
}
...
List<Repo> repos = gitHubEndpoint.getRepos();
request-headers: "Authorization: token abcdef...456789"
Assumes that an unexpired OAuth token is available, presumably fetched via cached credentials.