-
Notifications
You must be signed in to change notification settings - Fork 0
Retrofit
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.
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'
| 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 |
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 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.
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.
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.
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));
}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.
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
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";
}
}Writers can be used to write logs to a different destination than application logs.
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.createadded a third argument,RetrofitLoggingInterceptorand builder methodsloggingLevelandsuppressedHeadershave 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.
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.