DEV Community

paohaijiao
paohaijiao

Posted on

Interceptors That Actually Help: Request Logging and Automatic Bearer-Token Injection

Interceptors That Actually Help: Request Logging and Automatic Bearer-Token Injection

Cross-cutting HTTP concerns — logging, authentication headers, request IDs, metrics — should never be hand-edited into every request. JQuickCurl rides on OkHttp's mature interceptor pipeline, and because interceptors are wired into the global configuration, a single registration upgrades every curl command in your application.

In this post:

  • What an interceptor sees and can do.

  • The built-in JLoggingInterceptor.

  • Writing a request/response logging interceptor.

  • Auto-injecting a Bearer token from your secret store.

The Interceptor Model in One Paragraph

An OkHttp Interceptor wraps a call: it receives the outgoing Request, may modify it, calls chain.proceed(...), and then sees the returned Response. Registering interceptors happens on the global config, so both annotation-mode and XML-mode commands pass through them.

Interceptor myInterceptor = chain -> {
    Request request = chain.request();      // outgoing
    Response response = chain.proceed(request); // perform + get response
    return response;
};

JQuickCurlConfig.getInstance().addInterceptor(myInterceptor);
Enter fullscreen mode Exit fullscreen mode

addNetworkInterceptor(...) registers at the network layer (after redirects/retries) when you need to observe the real wire traffic.

Built-In Logging: JLoggingInterceptor

Out of the box, the config already registers a JLoggingInterceptor at level ALL — it measures each call and reports the elapsed time (and failures) through its console logger. If you want a quieter default, the same interceptor can be constructed with an explicit level:

public enum JCurlLevelLog {
    NONE, BASIC, HEADERS, ALL
}
Enter fullscreen mode Exit fullscreen mode
import com.github.paohaijiao.enums.JCurlLevelLog;
import com.github.paohaijiao.interceptor.JLoggingInterceptor;
import com.github.paohaijiao.config.JQuickCurlConfig;

JQuickCurlConfig.getInstance()
        .addInterceptor(new JLoggingInterceptor(JCurlLevelLog.BASIC));
Enter fullscreen mode Exit fullscreen mode

Expect output in the spirit of: the request cost : 132 ms. Choose the noisiest level in development, BASIC in staging, and keep an eye on token redaction (see below).

Writing a Purpose-Built Logging Interceptor

Build one that logs method, URL, status, and duration — and be deliberate about what you don't log (authorization headers!).

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class CallLogInterceptor implements Interceptor {

    private static final org.slf4j.Logger log =
            org.slf4j.LoggerFactory.getLogger(CallLogInterceptor.class);

    @Override
    public Response intercept(Chain chain) throws java.io.IOException {
        Request request = chain.request();
        long start = System.nanoTime();

        Response response = chain.proceed(request);

        long tookMs = java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(
                System.nanoTime() - start);
        String bodyPreview = "";
        if (response.body() != null) {
            bodyPreview = safePreview(response.peekBody(256).string());
        }

        log.info("[http] {} {} -> {} in {} ms body={}",
                request.method(), request.url(), response.code(), tookMs, bodyPreview);
        return response;
    }

    private String safePreview(String s) {
        return s == null ? "" : s.replace('\n', ' ').substring(
                0, Math.min(120, s.length()));
    }
}
Enter fullscreen mode Exit fullscreen mode

Register it next to the built-in one:

JQuickCurlConfig.getInstance().addInterceptor(new CallLogInterceptor());
Enter fullscreen mode Exit fullscreen mode

Automatic Bearer-Token Injection

Tokens expire, rotate, and come from secret managers — they don't belong in @JCurlCommand strings. One interceptor can stamp every outgoing request with the current token:

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

public class BearerAuthInterceptor implements Interceptor {

    private final java.util.function.Supplier<String> tokenSupplier;

    public BearerAuthInterceptor(java.util.function.Supplier<String> tokenSupplier) {
        this.tokenSupplier = tokenSupplier;
    }

    @Override
    public Response intercept(Chain chain) throws java.io.IOException {
        String token = tokenSupplier.get();
        Request request = chain.request().newBuilder()
                .addHeader("Authorization", "Bearer " + token)
                .build();
        return chain.proceed(request);
    }
}
Enter fullscreen mode Exit fullscreen mode

Because the token is fetched per request via a Supplier, rotating tokens are picked up automatically — no restart, no stale header. Wire it with your vault/environment source:

import com.github.paohaijiao.config.JQuickCurlConfig;

JQuickCurlConfig.getInstance().addInterceptor(
        new BearerAuthInterceptor(() -> System.getenv("API_TOKEN")));
Enter fullscreen mode Exit fullscreen mode

Now a plain command stays clean:

@JCurlCommand("curl -X GET 'https://api.example.com/me'")
String me(JQuickCurlReq request);   // Authorization stamped by interceptor
Enter fullscreen mode Exit fullscreen mode

The same trick works for API keys, X-Correlation-Id, tenant headers, and basic-auth pairs (Post 15).

Runnable Demo

import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.config.JQuickCurlConfig;
import com.github.paohaijiao.domain.req.JQuickCurlReq;
import com.github.paohaijiao.executor.JCurlInvoker;

public interface InspectApi {
    // httpbin.org/headers echoes every header the server received
    @JCurlCommand("curl -X GET 'https://httpbin.org/headers'")
    String headers(JQuickCurlReq request);
}

class InterceptorDemo {
    public static void main(String[] args) {
        JQuickCurlConfig.getInstance().addInterceptor(chain -> {
            okhttp3.Request r = chain.request().newBuilder()
                    .addHeader("Authorization", "Bearer demo-token-123")
                    .build();
            return chain.proceed(r);
        });

        String echo = JCurlInvoker.createProxy(InspectApi.class)
                .headers(new JQuickCurlReq());
        System.out.println(echo);   // "Authorization": "Bearer demo-token-123"
    }
}
Enter fullscreen mode Exit fullscreen mode

Ordering and Pitfalls

  • Interceptors run in registration order (application interceptors before network interceptors).

  • Headers you add in an interceptor are applied on top of whatever the curl string specified.

  • Never log or stash raw Authorization values — hash or redact in logs.

  • Keep interceptors fast; they run on the request thread.

Summary

Interceptors are the clean seam for concerns that cut across every HTTP call. JQuickCurl comes with timing-oriented logging out of the box and accepts any OkHttp interceptor through JQuickCurlConfig, so adding per-request Bearer tokens, correlation IDs, or metrics is a five-line change — while your curl commands stay declarative and secret-free.

Repository: dromara/jquick-curl. Post 13 shows per-method timeout overrides with @JTimeout when one stubborn endpoint needs different limits than the rest.

java #springboot #httpclient #opensource #java-library

Top comments (0)