Skip to content

Intercepting Requests

Lahiru Sahan Jayasinghe edited this page Jan 27, 2014 · 5 revisions

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.