Skip to content

Retrofit

Rain Ramm edited this page Nov 29, 2024 · 6 revisions

Given library allows to enhance the Retrofit library with auto-configuration, injection of interceptors and better integration with Spring Context and Core library api and exception definitions.

Enabling this feature

In order to enable the feature, following property should be added to application.properties file.

ee.bitweb.core.retrofit.auto-configuration=true

Following dependencies have to be added as library does not provide transitive dependencies:

    // https://mvnrepository.com/artifact/com.squareup.retrofit2/retrofit
    implementation group: 'com.squareup.retrofit2', name: 'retrofit', version: "${retrofitVersion}"

    // https://mvnrepository.com/artifact/com.squareup.retrofit2/converter-jackson
    implementation group: 'com.squareup.retrofit2', name: 'converter-jackson', version: "${retrofitVersion}"

Given feature will require ObjectMapper bean in the context. Given bean is created automatically with autoconfiguration from following dependency

    implementation 'org.springframework.boot:spring-boot-starter-web'

Available configuration properties

Property Default value Description
ee.bitweb.core.retrofit.auto-configuration false Enable Retrofit component autoloading to application context
ee.bitweb.core.retrofit.timeout.call 0 Time limit for a complete HTTP call, by defaultm has no limit.
ee.bitweb.core.retrofit.timeout.connect 10000 Time period in which client should establish a connection with a target host.
ee.bitweb.core.retrofit.timeout.read 10000 Maximum time of inactivity between two data packets when waiting for the server's response.
ee.bitweb.core.retrofit.timeout.write 10000 Maximum time of inactivity between two data packets when sending the request to the server.
ee.bitweb.core.retrofit.logging.level LoggingLevel.BASIC Logging detail level. See LoggingLevel enum for included mappers for every level.
ee.bitweb.core.retrofit.logging.maxLoggableRequestBodySize 10kB Maximum size of request body to be logged.
ee.bitweb.core.retrofit.logging.maxLoggableResponseBodySize 10kB Maximum size of response body to be logged.
ee.bitweb.core.retrofit.logging.suppressedHeaders   List of headers to exclude from logged request.
ee.bitweb.core.retrofit.logging.redactedBodyUrls   List of URLs to suppress body logging. Work for both request and response.
ee.bitweb.core.retrofit.logging.mappers Mappers defined in LoggingLevel enum Read more in mappers section
ee.bitweb.core.retrofit.authTokenInjector.enabled false Should builder add auth token injection interceptor to the api.
ee.bitweb.core.retrofit.authTokenInjector.headerName HttpHeaders.AUTHORIZATION Header to inject the authentication token

Builder

Given library provides ee.bitweb.core.retrofit.builder.RetrofitApiBuilder class which allows to configure Retrofit API conveniently. Also ee.bitweb.core.retrofit.builder.SpringAwareRetrofitBuilder is provided to automatically load properties, configurations, mappers and converters from Spring Context for more convenience.

Example

    @Bean
    public UserApi createUserApi(
            @Qualifier("userApiServiceTokenRetrofitRequestInterceptor") ServiceTokenRetrofitRequestInterceptor interceptor
    ) {
        log.info("Creating user api for {}", properties.getBaseUrl());

        return retrofitBuilder
                .create(properties.getBaseUrl(), UserApi.class)
                .add(interceptor)
                .build();
    }

Interceptors

Interceptors allow to modify the request/response of the retrofit api call. In order to conveniently add an interceptor to the builder you can define a bean which implements interface ee.bitweb.core.retrofit.interceptor.IntercetorBean.

Trace-Id iterceptor

If both ee.bitweb.core.trace.auto-configuration=true and ee.bitweb.core.retrofit.auto-configuration=true properties are set, then ee.bitweb.core.retrofit.interceptor.TraceIdInterceptor is automatically loaded to the list of preloaded interceptors for ee.bitweb.core.retrofit.builder.SpringAwareRetrofitBuilder.

That interceptor will propagate trace id onto the next service via the header configured in ee.bitweb.core.trace.invoker.http.TraceIdFilterConfig.

AuthToken inject interceptor

Basic ee.bitweb.core.retrofit.interceptor.auth.AuthTokenInjectInterceptor is also provided that can be used to propagate auth tokens between internal services. It can be auto-configured as default interceptor bean by setting property ee.bitweb.core.retrofit.auth-token-injector.enabled=true you will also need to specify the header name via ee.bitweb.core.retrofit.auth-token-injector.headerName property.

By default interceptor will test if the api call is eligible to propagate access token. This is done by testing request against ee.bitweb.core.retrofit.interceptor.auth.criteria.AuthTokenCriteria bean.

By default ee.bitweb.core.retrofit.interceptor.auth.criteria.WhitelistCriteria bean is added to the context. It will test the request against a whitelist of patterns. If request matches a pattern, access token will be injected.

A list of whitelist values should be provided, for example ee.bitweb.core.retrofit.auth-token-injector.whitelist-urls[0]=^http?:\\/\\/localhost:\\d{3,5}\\/.*

Given criteria has also a list of invalid pattern definitions to avoid lazy implementation for security reasons.

Rules for patterns are:

  • Invalid pattern endings: ["//.*"]
  • Invalid containments in pattern: ["./", "^https://localhost."]
  • Valid pattern prefixes: ["^http://", "^https://"]

If any rule is not followed an error is logged. In future releases an exception will be thrown.

Lastly, you must provide implementation of ee.bitweb.core.retrofit.interceptor.auth.TokenProvider interface. That class defines how to extract token from security context or properties class. In case of an auto-configuration, it must be declared as bean.

Executor

Library provides an Executor class that should be used to execute an API call. It provides some rudimentary response data extraction and performs some basic error handling. If an error does happen during the API call, an ee.bitweb.core.retrofit.RetrofitException will be thrown.

Sample

  public UserResponse byPersonCode(String personCode) {
      log.info("Requesting user from user server by personCode: {}", personCode);

      return RetrofitRequestExecutor.execute(userApi.getByPersonCode(personCode));
  }

Logging

This library offers a solution to replace the default Retrofit logger with a logger that is more configurable and extendable. For example, one can control what data is written to logs by choosing what mappers are used or by writing custom mappers. Logging destination can also be changed by using custom writers. By default log is written to application log with single entry and all data from mappers added to MDC.

PS! Please exclude any URLs from body logging when moving large amount of data in body. For example, file upload and download. The current solution may cause OOM exceptions when handling and logging large bodies.

Mappers

By default, mappers defined in LoggingLevel enum are used. When logging level is set to CUSTOM then one must provide a list of mappers, otherwise nothing is logged. Logging levels can be customised by adding specific mappers in config. As long as logging level is not CUSTOM, default and provided mappers are merged. For example, when you want basic logging with response body, the configuration will achieve this:

ee.bitweb.core.retrofit.logging.level=basic
ee.bitweb.core.retrofit.logging.mappers=response_body

Creating a custom mapper

Custom mappers can be created by implementing the RetrofitLoggingMapper class, adding it as a Spring component and changing the configuration to include new mapper:

@Slf4j
@Component
public class ContentTypeRetrofitLoggerMapper implements RetrofitLoggingMapper {

    @Override
    public String getValue(Request request, Response response) {
        return request.header("Content-Type");
    }

    @Override
    public String getKey() {
        return "ContentType";
    }
}

Creating a custom writer

Writers can be used to write logs to a different destination than application logs.

Migration from 3.* to 4.*

Breaking changes:

  • Logs are written to a single log entry now. Data from mappers is mostly added to MDC. The new log message looks as follows for every log level: GET https://localhost:3000/api?data=true&test 404 1024bytes 14ms
  • RetrofitApiBuilder.create added a third argument, RetrofitLoggingInterceptor and builder methods loggingLevel and suppressedHeaders have been removed.

When using Spring aware Retrofit builder, then all changes have been made for you. And nothing needs to be changed in application code.

Migration from 2.* to 3.*

In order to enable AuthTokenInjector component, change ee.bitweb.core.retrofit.auth-token-injector.enabled=true to ee.bitweb.core.retrofit.auth-token-injector.auto-configuration=true.

Clone this wiki locally