扔掉okhttp、httpClient,來試試這款輕量級 HTTP 客戶端框架,吹爆!
在SpringBoot項目直接使用okhttp、httpClient或者RestTemplate發起HTTP請求,既繁瑣又不方便統一管理。
因此,在這里推薦一個適用于SpringBoot項目的輕量級HTTP客戶端框架retrofit-spring-boot-starter,使用非常簡單方便,同時又提供諸多功能增強。
適用于retrofit的spring-boot-starter,支持快速集成和功能增強。
- Spring Boot 3.x 項目,請使用retrofit-spring-boot-starter 3.x。
- Spring Boot 1.x/2.x 項目,請使用retrofit-spring-boot-starter 2.x。
github項目地址:https://github.com/LianjiaTech/retrofit-spring-boot-starter
gitee項目地址:https://gitee.com/lianjiatech/retrofit-spring-boot-starter
功能特性
- 自定義OkHttpClient
- 注解式攔截器
- 日志打印
- 請求重試
- 熔斷降級
- 錯誤解碼器
- 微服務之間的HTTP調用
- 全局攔截器
- 調用適配器
- 數據轉換器
- 元注解
- 其他功能示例
快速開始
引入依賴
<dependency>
<groupId>com.github.lianjiatech</groupId>
<artifactId>retrofit-spring-boot-starter</artifactId>
<version>3.0.2</version>
</dependency>
如果啟動失敗,大概率是依賴沖突,煩請引入或者排除相關依賴。
定義HTTP接口
接口必須使用@RetrofitClient注解標記!
@RetrofitClient(baseUrl = "${test.baseUrl}")
public interface HttpApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}
注意:方法請求路徑慎用/開頭。對于Retrofit而言,如果baseUrl=http://localhost:8080/api/test/,方法請求路徑如果是person,則該方法完整的請求路徑是:http://localhost:8080/api/test/person。而方法請求路徑如果是/person,則該方法完整的請求路徑是:http://localhost:8080/person。
注入使用
將接口注入到其它Service中即可使用!
@Service
public class TestService {
@Autowired
private HttpApi httpApi;
public void test() {
// 使用`httpApi`發起HTTP請求
}
}
默認情況下,自動使用SpringBoot掃描路徑進行RetrofitClient注冊。你也可以在配置類加上@RetrofitScan手工指定掃描路徑。
HTTP請求相關注解
HTTP請求相關注解,全部使用了Retrofit原生注解,以下是一個簡單說明:
注解分類 | 支持的注解 |
請求方式 |
|
請求頭 |
|
Query參數 |
|
path參數 |
|
form-encoded參數 |
|
請求體 |
|
文件上傳 |
|
url參數 |
|
配置屬性
組件支持了多個可配置的屬性,用來應對不同的業務場景,具體可支持的配置屬性及默認值如下:
注意:應用只需要配置要更改的配置項!
retrofit:
# 全局轉換器工廠
global-converter-factories:
-com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
-retrofit2.converter.jackson.JacksonConverterFactory
# 全局調用適配器工廠(組件擴展的調用適配器工廠已經內置,這里請勿重復配置)
global-call-adapter-factories:
# 全局日志打印配置
global-log:
# 啟用日志打印
enable:true
# 全局日志打印級別
log-level:info
# 全局日志打印策略
log-strategy:basic
# 全局重試配置
global-retry:
# 是否啟用全局重試
enable:false
# 全局重試間隔時間
interval-ms:100
# 全局最大重試次數
max-retries:2
# 全局重試規則
retry-rules:
-response_status_not_2xx
-occur_io_exception
# 全局超時時間配置
global-timeout:
# 全局讀取超時時間
read-timeout-ms:10000
# 全局寫入超時時間
write-timeout-ms:10000
# 全局連接超時時間
connect-timeout-ms:10000
# 全局完整調用超時時間
call-timeout-ms:0
# 熔斷降級配置
degrade:
# 熔斷降級類型。默認none,表示不啟用熔斷降級
degrade-type:none
# 全局sentinel降級配置
global-sentinel-degrade:
# 是否開啟
enable:false
# 各降級策略對應的閾值。平均響應時間(ms),異常比例(0-1),異常數量(1-N)
count:1000
# 熔斷時長,單位為 s
time-window:5
# 降級策略(0:平均響應時間;1:異常比例;2:異常數量)
grade:0
# 全局resilience4j降級配置
global-resilience4j-degrade:
# 是否開啟
enable:false
# 根據該名稱從#{@link CircuitBreakerConfigRegistry}獲取CircuitBreakerConfig,作為全局熔斷配置
circuit-breaker-config-name:defaultCircuitBreakerConfig
# 自動設置PathMathInterceptor的scope為prototype
auto-set-prototype-scope-for-path-math-interceptor:true
高級功能
超時時間配置
如果僅僅需要修改OkHttpClient的超時時間,可以通過@RetrofitClient相關字段修改,或者全局超時配置修改。
自定義OkHttpClient
如果需要修改OkHttpClient其它配置,可以通過自定義OkHttpClient來實現,步驟如下:
1.實現SourceOkHttpClientRegistrar接口,調用SourceOkHttpClientRegistry#register()方法注冊OkHttpClient。
@Slf4j
@Component
publicclass CustomSourceOkHttpClientRegistrar implements SourceOkHttpClientRegistrar {
@Override
public void register(SourceOkHttpClientRegistry registry) {
// 添加testSourceOkHttpClient
registry.register("testSourceOkHttpClient", new OkHttpClient.Builder()
.connectTimeout(Duration.ofSeconds(3))
.writeTimeout(Duration.ofSeconds(3))
.readTimeout(Duration.ofSeconds(3))
.addInterceptor(chain -> {
log.info("============use testSourceOkHttpClient=============");
return chain.proceed(chain.request());
})
.build());
}
}
2.通過@RetrofitClient.sourceOkHttpClient指定當前接口要使用的OkHttpClient。
@RetrofitClient(baseUrl = "${test.baseUrl}", sourceOkHttpClient = "testSourceOkHttpClient")
public interface CustomOkHttpTestApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
}
注意:組件不會直接使用指定的OkHttpClient,而是基于該OkHttpClient創建一個新的。
注解式攔截器
組件提供了注解式攔截器,支持基于url路徑匹配攔截,使用的步驟如下:
- 繼承BasePathMatchInterceptor
- 使用@Intercept注解指定要使用的攔截器
如果需要使用多個攔截器,在接口上標注多個@Intercept注解即可。
下面以"給指定請求的url后面拼接timestamp時間戳"為例,介紹下如何使用注解式攔截器。
繼承BasePathMatchInterceptor編寫攔截處理器
@Component
publicclass TimeStampInterceptor extends BasePathMatchInterceptor {
@Override
public Response doIntercept(Chain chain) throws IOException {
Request request = chain.request();
HttpUrl url = request.url();
long timestamp = System.currentTimeMillis();
HttpUrl newUrl = url.newBuilder()
.addQueryParameter("timestamp", String.valueOf(timestamp))
.build();
Request newRequest = request.newBuilder()
.url(newUrl)
.build();
return chain.proceed(newRequest);
}
}
默認情況下,組件會自動將BasePathMatchInterceptor的scope設置為prototype。
可通過retrofit.auto-set-prototype-scope-for-path-math-interceptor=false關閉該功能。關閉之后,需要手動將scope設置為prototype。
@Component
@Scope("prototype")
public class TimeStampInterceptor extends BasePathMatchInterceptor {
@Override
public Response doIntercept(Chain chain) throws IOException {
// ...
}
}
接口上使用@Intercept進行標注
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Intercept(handler = TimeStampInterceptor.class, include = {"/api/**"}, exclude = "/api/test/savePerson")
@Intercept(handler = TimeStamp2Interceptor.class) // 需要多個,直接添加即可
public interface HttpApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
@POST("savePerson")
Result<Person> savePerson(@Body Person person);
}
上面的@Intercept配置表示:攔截HttpApi接口下/api/**路徑下(排除/api/test/savePerson)的請求,攔截處理器使用TimeStampInterceptor。
自定義攔截注解
有的時候,我們需要在"攔截注解"動態傳入一些參數,然后在攔截的時候使用這些參數。這時候,我們可以使用"自定義攔截注解",步驟如下:
- 自定義注解。必須使用@InterceptMark標記,并且注解中必須包括include、exclude、handler字段。
- 繼承BasePathMatchInterceptor編寫攔截處理器
- 接口上使用自定義注解
例如,我們需要"在請求頭里面動態加入accessKeyId、accessKeySecret簽名信息才能再發起HTTP請求",這時候可以自定義@Sign注解來實現。
自定義@Sign注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@InterceptMark
public@interface Sign {
String accessKeyId();
String accessKeySecret();
String[] include() default {"/**"};
String[] exclude() default {};
Class<? extends BasePathMatchInterceptor> handler() default SignInterceptor.class;
}
在@Sign注解中指定了使用的攔截器是SignInterceptor。
實現SignInterceptor
@Component
publicclass SignInterceptor extends BasePathMatchInterceptor {
private String accessKeyId;
private String accessKeySecret;
public void setAccessKeyId(String accessKeyId) {
this.accessKeyId = accessKeyId;
}
public void setAccessKeySecret(String accessKeySecret) {
this.accessKeySecret = accessKeySecret;
}
@Override
public Response doIntercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("accessKeyId", accessKeyId)
.addHeader("accessKeySecret", accessKeySecret)
.build();
return chain.proceed(newReq);
}
}
注意:accessKeyId和accessKeySecret字段必須提供setter方法。
攔截器的accessKeyId和accessKeySecret字段值會依據@Sign注解的accessKeyId()和accessKeySecret()值自動注入,如果@Sign指定的是占位符形式的字符串,則會取配置屬性值進行注入。
接口上使用@Sign
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Sign(accessKeyId = "${test.accessKeyId}", accessKeySecret = "${test.accessKeySecret}", exclude = {"/api/test/person"})
public interface HttpApi {
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
@POST("savePerson")
Result<Person> savePerson(@Body Person person);
}
日志打印
組件支持支持全局日志打印和聲明式日志打印。
全局日志打印
默認情況下,全局日志打印是開啟的,默認配置如下:
retrofit:
# 全局日志打印配置
global-log:
# 啟用日志打印
enable: true
# 全局日志打印級別
log-level: info
# 全局日志打印策略
log-strategy: basic
四種日志打印策略含義如下:
- NONE:No logs.
- BASIC:Logs request and response lines.
- HEADERS:Logs request and response lines and their respective headers.
- BODY:Logs request and response lines and their respective headers and bodies (if present).
聲明式日志打印
如果只需要部分請求才打印日志,可以在相關接口或者方法上使用@Logging注解。
日志打印自定義擴展
如果需要修改日志打印行為,可以繼承LoggingInterceptor,并將其配置成Spring bean。
聚合日志打印
如果需要將同一個請求的日志聚合在一起打印,可配置AggregateLoggingInterceptor。
@Bean
public LoggingInterceptor loggingInterceptor(RetrofitProperties retrofitProperties){
return new AggregateLoggingInterceptor(retrofitProperties.getGlobalLog());
}
請求重試
組件支持支持全局重試和聲明式重試。
全局重試
全局重試默認關閉,默認配置項如下:
retrofit:
# 全局重試配置
global-retry:
# 是否啟用全局重試
enable: false
# 全局重試間隔時間
interval-ms: 100
# 全局最大重試次數
max-retries: 2
# 全局重試規則
retry-rules:
- response_status_not_2xx
- occur_io_exception
重試規則支持三種配置:
- RESPONSE_STATUS_NOT_2XX:響應狀態碼不是2xx時執行重試
- OCCUR_IO_EXCEPTION:發生IO異常時執行重試
- OCCUR_EXCEPTION:發生任意異常時執行重試
聲明式重試
如果只有一部分請求需要重試,可以在相應的接口或者方法上使用@Retry注解。
請求重試自定義擴展
如果需要修改請求重試行為,可以繼承RetryInterceptor,并將其配置成Spring bean。
熔斷降級
熔斷降級默認關閉,當前支持sentinel和resilience4j兩種實現。
retrofit:
# 熔斷降級配置
degrade:
# 熔斷降級類型。默認none,表示不啟用熔斷降級
degrade-type: sentinel
Sentinel
配置degrade-type=sentinel開啟,然后在相關接口或者方法上聲明@SentinelDegrade注解即可。
記得手動引入Sentinel依賴:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.6.3</version>
</dependency>
此外,還支持全局Sentinel熔斷降級:
retrofit:
# 熔斷降級配置
degrade:
# 熔斷降級類型。默認none,表示不啟用熔斷降級
degrade-type: sentinel
# 全局sentinel降級配置
global-sentinel-degrade:
# 是否開啟
enable: true
# ...其他sentinel全局配置
Resilience4j
配置degrade-type=resilience4j開啟。然后在相關接口或者方法上聲明@Resilience4jDegrade即可。
記得手動引入Resilience4j依賴:
<dependency>
<groupId>io.github.resilience4j</groupId>
<artifactId>resilience4j-circuitbreaker</artifactId>
<version>1.7.1</version>
</dependency>
通過以下配置可開啟全局resilience4j熔斷降級:
retrofit:
# 熔斷降級配置
degrade:
# 熔斷降級類型。默認none,表示不啟用熔斷降級
degrade-type: resilience4j
# 全局resilience4j降級配置
global-resilience4j-degrade:
# 是否開啟
enable: true
# 根據該名稱從#{@link CircuitBreakerConfigRegistry}獲取CircuitBreakerConfig,作為全局熔斷配置
circuit-breaker-config-name: defaultCircuitBreakerConfig
熔斷配置管理:
1.實現CircuitBreakerConfigRegistrar接口,注冊CircuitBreakerConfig。
@Component
publicclass CustomCircuitBreakerConfigRegistrar implements CircuitBreakerConfigRegistrar {
@Override
public void register(CircuitBreakerConfigRegistry registry) {
// 替換默認的CircuitBreakerConfig
registry.register(Constants.DEFAULT_CIRCUIT_BREAKER_CONFIG, CircuitBreakerConfig.ofDefaults());
// 注冊其它的CircuitBreakerConfig
registry.register("testCircuitBreakerConfig", CircuitBreakerConfig.custom()
.slidingWindowType(CircuitBreakerConfig.SlidingWindowType.TIME_BASED)
.failureRateThreshold(20)
.minimumNumberOfCalls(5)
.permittedNumberOfCallsInHalfOpenState(5)
.build());
}
}
2.通過circuitBreakerConfigName指定CircuitBreakerConfig。包括retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name或者@Resilience4jDegrade.circuitBreakerConfigName
擴展熔斷降級
如果用戶需要使用其他的熔斷降級實現,繼承BaseRetrofitDegrade,并將其配置Spring Bean。
配置fallback或者fallbackFactory (可選)
如果@RetrofitClient不設置fallback或者fallbackFactory,當觸發熔斷時,會直接拋出RetrofitBlockException異常。用戶可以通過設置fallback或者fallbackFactory來定制熔斷時的方法返回值。
注意:fallback類必須是當前接口的實現類,fallbackFactory必須是FallbackFactory<T>實現類,泛型參數類型為當前接口類型。另外,fallback和fallbackFactory實例必須配置成Spring Bean。
fallbackFactory相對于fallback,主要差別在于能夠感知每次熔斷的異常原因(cause),參考示例如下:
@Slf4j
@Service
publicclass HttpDegradeFallback implements HttpDegradeApi {
@Override
public Result<Integer> test() {
Result<Integer> fallback = new Result<>();
fallback.setCode(100)
.setMsg("fallback")
.setBody(1000000);
return fallback;
}
}
@Slf4j
@Service
publicclass HttpDegradeFallbackFactory implements FallbackFactory<HttpDegradeApi> {
@Override
public HttpDegradeApi create(Throwable cause) {
log.error("觸發熔斷了! ", cause.getMessage(), cause);
returnnew HttpDegradeApi() {
@Override
public Result<Integer> test() {
Result<Integer> fallback = new Result<>();
fallback.setCode(100)
.setMsg("fallback")
.setBody(1000000);
return fallback;
}
};
}
}
錯誤解碼器
在HTTP發生請求錯誤(包括發生異常或者響應數據不符合預期)的時候,錯誤解碼器可將HTTP相關信息解碼到自定義異常中。你可以在@RetrofitClient注解的errorDecoder()指定當前接口的錯誤解碼器,自定義錯誤解碼器需要實現ErrorDecoder接口:
微服務之間的HTTP調用
繼承ServiceInstanceChooser
用戶可以自行實現ServiceInstanceChooser接口,完成服務實例的選取邏輯,并將其配置成Spring Bean。對于Spring Cloud應用,可以使用如下實現。
@Service
publicclass SpringCloudServiceInstanceChooser implements ServiceInstanceChooser {
private LoadBalancerClient loadBalancerClient;
@Autowired
public SpringCloudServiceInstanceChooser(LoadBalancerClient loadBalancerClient) {
this.loadBalancerClient = loadBalancerClient;
}
/**
* Chooses a ServiceInstance URI from the LoadBalancer for the specified service.
*
* @param serviceId The service ID to look up the LoadBalancer.
* @return Return the uri of ServiceInstance
*/
@Override
public URI choose(String serviceId) {
ServiceInstance serviceInstance = loadBalancerClient.choose(serviceId);
Assert.notNull(serviceInstance, "can not found service instance! serviceId=" + serviceId);
return serviceInstance.getUri();
}
}
指定serviceId和path
@RetrofitClient(serviceId = "${jy-helicarrier-api.serviceId}", path = "/m/count")
public interface ApiCountService {}
全局攔截器
全局應用攔截器
如果我們需要對整個系統的的HTTP請求執行統一的攔截處理,可以實現全局攔截器GlobalInterceptor, 并配置成spring Bean。
@Component
publicclass SourceGlobalInterceptor implements GlobalInterceptor {
@Autowired
private TestService testService;
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Request newReq = request.newBuilder()
.addHeader("source", "test")
.build();
testService.test();
return chain.proceed(newReq);
}
}
全局網絡攔截器
實現NetworkInterceptor接口,并配置成spring Bean。
調用適配器
Retrofit可以通過CallAdapterFactory將Call<T>對象適配成接口方法的返回值類型。組件擴展了一些CallAdapterFactory實現:
- BodyCallAdapterFactory
- 同步執行HTTP請求,將響應體內容適配成方法的返回值類型。
任意方法返回值類型都可以使用BodyCallAdapterFactory,優先級最低。
- ResponseCallAdapterFactory
- 同步執行HTTP請求,將響應體內容適配成Retrofit.Response<T>返回。
- 只有方法返回值類型為Retrofit.Response<T>,才可以使用ResponseCallAdapterFactory。
- 響應式編程相關CallAdapterFactory
Retrofit會根據方法返回值類型選擇對應的CallAdapterFactory執行適配處理,目前支持的返回值類型如下:
- String:將Response Body適配成String返回。
- 基礎類型(Long/Integer/Boolean/Float/Double):將Response Body適配成上述基礎類型
- 任意Java類型:將Response Body適配成對應的Java對象返回
- CompletableFuture<T>: 將Response Body適配成CompletableFuture<T>對象返回
- Void: 不關注返回類型可以使用Void
- Response<T>: 將Response適配成Response<T>對象返回
- Call<T>: 不執行適配處理,直接返回Call<T>對象
- Mono<T>: Project Reactor響應式返回類型
- Single<T>:Rxjava響應式返回類型(支持Rxjava2/Rxjava3)
- Completable:Rxjava響應式返回類型,HTTP請求沒有響應體(支持Rxjava2/Rxjava3)
@RetrofitClient(baseUrl = "${test.baseUrl}")
publicinterface HttpApi {
@POST("getString")
String getString(@Body Person person);
@GET("person")
Result<Person> getPerson(@Query("id") Long id);
@GET("person")
CompletableFuture<Result<Person>> getPersonCompletableFuture(@Query("id") Long id);
@POST("savePerson")
Void savePersonVoid(@Body Person person);
@GET("person")
Response<Result<Person>> getPersonResponse(@Query("id") Long id);
@GET("person")
Call<Result<Person>> getPersonCall(@Query("id") Long id);
@GET("person")
Mono<Result<Person>> monoPerson(@Query("id") Long id);
@GET("person")
Single<Result<Person>> singlePerson(@Query("id") Long id);
@GET("ping")
Completable ping();
}
可以通過繼承CallAdapter.Factory擴展CallAdapter。
組件支持通過retrofit.global-call-adapter-factories配置全局調用適配器工廠:
retrofit:
# 全局轉換器工廠(組件擴展的`CallAdaptorFactory`工廠已經內置,這里請勿重復配置)
global-call-adapter-factories:
# ...
針對每個Java接口,還可以通過@RetrofitClient.callAdapterFactories指定當前接口采用的CallAdapter.Factory。
建議:將CallAdapter.Factory配置成Spring Bean
數據轉碼器
Retrofit使用Converter將@Body注解的對象轉換成Request Body,將Response Body轉換成一個Java對象,可以選用以下幾種Converter:
- Gson: com.squareup.Retrofit:converter-gson
- Jackson: com.squareup.Retrofit:converter-jackson
- Moshi: com.squareup.Retrofit:converter-moshi
- Protobuf: com.squareup.Retrofit:converter-protobuf
- Wire: com.squareup.Retrofit:converter-wire
- Simple XML: com.squareup.Retrofit:converter-simplexml
- JAXB: com.squareup.retrofit2:converter-jaxb
- fastJson:com.alibaba.fastjson.support.retrofit.Retrofit2ConverterFactory
組件支持通過retrofit.global-converter-factories配置全局Converter.Factory,默認的是retrofit2.converter.jackson.JacksonConverterFactory。
如果需要修改Jackson配置,自行覆蓋JacksonConverterFactory的bean配置即可。
retrofit:
# 全局轉換器工廠
global-converter-factories:
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
針對每個Java接口,還可以通過@RetrofitClient.converterFactories指定當前接口采用的Converter.Factory。
建議:將Converter.Factory配置成Spring Bean。
元注解
@RetrofitClient、@Retry、@Logging、@Resilience4jDegrade等注解支持元注解、繼承以及@AliasFor。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
@RetrofitClient(baseUrl = "${test.baseUrl}")
@Logging(logLevel = LogLevel.WARN)
@Retry(intervalMs = 200)
public@interface MyRetrofitClient {
@AliasFor(annotation = RetrofitClient.class, attribute = "converterFactories")
Class<? extends Converter.Factory>[] converterFactories() default {GsonConverterFactory.class};
@AliasFor(annotation = Logging.class, attribute = "logStrategy")
LogStrategy logStrategy() default LogStrategy.BODY;
}
其他功能示例
form參數
@FormUrlEncoded
@POST("token/verify")
Object tokenVerify(@Field("source") String source,@Field("signature") String signature,@Field("token") String token);
@FormUrlEncoded
@POST("message")
CompletableFuture<Object> sendMessage(@FieldMap Map<String, Object> param);
文件上傳
創建MultipartBody.Part
// 對文件名使用URLEncoder進行編碼
public ResponseEntity importTerminology(MultipartFile file){
String fileName=URLEncoder.encode(Objects.requireNonNull(file.getOriginalFilename()),"utf-8");
okhttp3.RequestBody requestBody=okhttp3.RequestBody.create(MediaType.parse("multipart/form-data"),file.getBytes());
MultipartBody.Part part=MultipartBody.Part.createFormData("file",fileName,requestBody);
apiService.upload(part);
return ok().build();
}
HTTP上傳接口
@POST("upload")
@Multipart
Void upload(@Part MultipartBody.Part file);
文件下載
HTTP下載接口
@RetrofitClient(baseUrl = "https://.ljcdn.com/hc-picture/")
public interface DownloadApi {
@GET("{fileKey}")
Response<ResponseBody> download(@Path("fileKey") String fileKey);
}
HTTP下載使用
@SpringBootTest(classes = RetrofitTestApplication.class)
@RunWith(SpringRunner.class)
public class DownloadTest {
@Autowired
DownloadApi downLoadApi;
@Test
public void download() throws Exception {
String fileKey = "6302d742-ebc8-4649-95cf-62ccf57a1add";
Response<ResponseBody> response = downLoadApi.download(fileKey);
ResponseBody responseBody = response.body();
// 二進制流
InputStream is = responseBody.byteStream();
// 具體如何處理二進制流,由業務自行控制。這里以寫入文件為例
File tempDirectory = new File("temp");
if (!tempDirectory.exists()) {
tempDirectory.mkdir();
}
File file = new File(tempDirectory, UUID.randomUUID().toString());
if (!file.exists()) {
file.createNewFile();
}
FileOutputStream fos = new FileOutputStream(file);
byte[] b = newbyte[1024];
int length;
while ((length = is.read(b)) > 0) {
fos.write(b, 0, length);
}
is.close();
fos.close();
}
}
動態URL
使用@url注解可實現動態URL。此時,baseUrl配置任意合法url即可。例如:http://github.com/ 。運行時只會根據@Url地址發起請求。
注意:@url必須放在方法參數的第一個位置,另外,@GET、@POST等注解上,不需要定義端點路徑。
@GET
Map<String, Object> test3(@Url String url,@Query("name") String name);
DELETE請求添加請求體
@HTTP(method = "DELETE", path = "/user/delete", hasBody = true)
GET請求添加請求體
okhttp3自身不支持GET請求添加請求體,源碼如下:
圖片
圖片
作者給出了具體原因,可以參考:https://github.com/square/okhttp/issues/3154
但是,如果實在需要這么做,可以使用:@HTTP(method = "get", path = "/user/get", hasBody = true),使用小寫get繞過上述限制。