精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

扔掉okhttp、httpClient,來試試這款輕量級 HTTP 客戶端框架,吹爆!

開發 前端
自動使用?SpringBoot?掃描路徑進行?RetrofitClient?注冊。你也可以在配置類加上@RetrofitScan手工指定掃描路徑。

在SpringBoot項目直接使用okhttp、httpClient或者RestTemplate發起HTTP請求,既繁瑣又不方便統一管理。

因此,在這里推薦一個適用于SpringBoot項目的輕量級HTTP客戶端框架retrofit-spring-boot-starter,使用非常簡單方便,同時又提供諸多功能增強。

適用于retrofit的spring-boot-starter,支持快速集成和功能增強。

  1. Spring Boot 3.x 項目,請使用retrofit-spring-boot-starter 3.x。
  2. 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原生注解,以下是一個簡單說明:

注解分類

支持的注解

請求方式

@GET@HEAD@POST@PUT@DELETE@OPTIONS@HTTP

請求頭

@Header@HeaderMap@Headers

Query參數

@Query@QueryMap@QueryName

path參數

@Path

form-encoded參數

@Field@FieldMap@FormUrlEncoded

請求體

@Body

文件上傳

@Multipart@Part@PartMap

url參數

@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路徑匹配攔截,使用的步驟如下:

  1. 繼承BasePathMatchInterceptor
  2. 使用@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。

自定義攔截注解

有的時候,我們需要在"攔截注解"動態傳入一些參數,然后在攔截的時候使用這些參數。這時候,我們可以使用"自定義攔截注解",步驟如下:

  1. 自定義注解。必須使用@InterceptMark標記,并且注解中必須包括include、exclude、handler字段。
  2. 繼承BasePathMatchInterceptor編寫攔截處理器
  3. 接口上使用自定義注解

例如,我們需要"在請求頭里面動態加入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

四種日志打印策略含義如下:

  1. NONE:No logs.
  2. BASIC:Logs request and response lines.
  3. HEADERS:Logs request and response lines and their respective headers.
  4. 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

重試規則支持三種配置:

  1. RESPONSE_STATUS_NOT_2XX:響應狀態碼不是2xx時執行重試
  2. OCCUR_IO_EXCEPTION:發生IO異常時執行重試
  3. 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實現:

  1. BodyCallAdapterFactory
  2. 同步執行HTTP請求,將響應體內容適配成方法的返回值類型。

任意方法返回值類型都可以使用BodyCallAdapterFactory,優先級最低。

  1. ResponseCallAdapterFactory
  2. 同步執行HTTP請求,將響應體內容適配成Retrofit.Response<T>返回。
  • 只有方法返回值類型為Retrofit.Response<T>,才可以使用ResponseCallAdapterFactory。
  1. 響應式編程相關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繞過上述限制。

責任編輯:武曉燕 來源: 碼猿技術專欄
相關推薦

2021-04-22 08:33:00

ForestHTTPAPI框

2020-03-24 15:15:29

HttpClientOkHttpJava

2022-03-08 13:46:22

httpClientHTTP前端

2024-05-09 08:30:57

OkHttpHTTP客戶端

2025-08-01 09:38:00

2022-05-16 07:37:58

SQL 編輯器數據庫管理工具

2023-08-09 08:01:38

場景Redis接口

2012-07-18 10:09:55

輕量級移動客戶端開發類庫

2014-07-17 15:47:52

2024-04-29 08:42:23

2012-11-28 11:05:42

IBMdW

2023-10-30 11:28:33

Kubernetes負載均衡

2022-09-05 09:37:38

Linux發行版

2021-05-21 10:48:09

http語言開發

2009-06-12 19:18:08

REST客戶端框架JavaScript

2021-10-18 05:00:38

語言GoRequestHTTP

2023-06-27 16:42:18

Tinygrad深度學習工具

2020-11-11 12:13:59

JS

2022-08-10 12:21:07

PythonWebBottle

2024-11-13 16:32:21

aviatorJava表達式引擎
點贊
收藏

51CTO技術棧公眾號

欧美精品一卡两卡| 国产欧美一区二区精品婷婷 | 亚洲www在线观看| 中文字幕亚洲欧美日韩| 96sao在线精品免费视频| 午夜电影一区二区三区| 性刺激综合网| 丰满大乳国产精品| 全国精品久久少妇| 久99久在线视频| 欧美特级黄色录像| 日韩三级久久| 色www精品视频在线观看| 美国av在线播放| 四虎免费在线观看| 国产一区二区三区精品视频| 2019日本中文字幕| 婷婷伊人五月天| 蜜臀av免费一区二区三区| 在线不卡免费av| 免费看日本毛片| 麻豆传媒在线免费看| 9l国产精品久久久久麻豆| 国产有码在线一区二区视频| 日产精品久久久| 午夜久久美女| 久久精品99久久久香蕉| 性少妇bbw张开| 成人18夜夜网深夜福利网| 欧美日韩激情在线| 欧美日韩第二页| 国产h片在线观看| 亚洲欧美二区三区| 日韩中文一区二区三区| 天天干天天操av| 国产精品1区2区| 国产美女扒开尿口久久久| 国产无人区码熟妇毛片多| 欧美国产高清| 久久久成人精品视频| 99在线视频免费| 人人精品视频| 亚洲丁香久久久| 麻豆精品国产传媒| 国产日韩欧美中文在线| 欧美日韩亚洲综合在线| 91色国产在线| 外国成人直播| 一本大道久久a久久精品综合| 国产a级片网站| 日本在线观看高清完整版| 日韩毛片一二三区| 国产精品jizz在线观看老狼| 午夜免费福利在线观看| 国产精品人人做人人爽人人添| 久久精品国产99精品国产亚洲性色| www.国产免费| 高清国产一区二区三区| 99热在线播放| 亚洲AV无码国产精品午夜字幕 | 免费动漫网站在线观看| 99久久精品国产导航| 国产一区免费| 天堂中文资源在线| 久久蜜桃av一区二区天堂| 蜜桃av久久久亚洲精品| 欧美挠脚心网站| 国产欧美视频一区二区| 五月天久久狠狠| 日韩三级影院| 樱桃视频在线观看一区| 久久亚洲国产成人精品无码区| 中文字幕免费高清电视剧网站在线观看| 亚洲欧洲日本在线| 国内外成人激情免费视频| 亚洲色图美国十次| 亚洲香肠在线观看| 亚欧无线一线二线三线区别| 成人性生活视频| 欧美亚洲国产一区二区三区| 天天影视色综合| 伊人久久影院| 亚洲九九九在线观看| 欧美激情视频二区| 欧美在线二区| 奇米四色中文综合久久| 中文字幕日韩第一页| 国产一区二区在线观看免费| 国产精品一区二区欧美黑人喷潮水| 午夜小视频在线播放| 国产日韩欧美精品综合| 91精品国产吴梦梦| 中文字幕在线官网| 欧美美女视频在线观看| 国产大学生视频| 男人天堂av片| 无码国产精品一区二区色情男同| 26uuu亚洲综合色欧美| 日韩精品在在线一区二区中文| 1769在线观看| 亚洲自拍与偷拍| 欧美成人精品欧美一级乱| 亚洲欧美久久精品| 日韩av在线看| 男人的午夜天堂| 亚洲欧洲一级| 成人精品aaaa网站| 天天综合在线视频| **性色生活片久久毛片| 黄色动漫在线免费看| 亚洲天堂网站| 亚洲欧美日韩网| 免费在线看黄网址| 日韩av电影天堂| 成人性色av| 无遮挡动作视频在线观看免费入口| 一区二区高清在线| 九色porny91| 国产亚洲精品美女久久| 久久精品视频在线播放| 国产精品久久久久久人| 国产成人免费网站| 亚洲精品无人区| 欧美18av| 日韩大片免费观看视频播放| 青青草国产在线观看| 男女男精品网站| 免费国产在线精品一区二区三区| 91福利国产在线观看菠萝蜜| 欧美系列日韩一区| 国产美女喷水视频| 在线观看一区视频| 99在线视频播放| 免费在线视频欧美| 在线精品观看国产| 亚洲熟妇一区二区三区| 国内综合精品午夜久久资源| 国产欧美精品va在线观看| 国产三级视频在线看| 福利视频导航一区| 尤物网站在线观看| 亚洲国产专区| 国产精品制服诱惑| 黑人玩欧美人三根一起进| 91精品国产福利| 国产精品精品软件男同| 蜜臀a∨国产成人精品| 日韩免费av电影| 精品123区| 亚洲精品一区二区网址 | 亚洲高清免费观看| 91亚洲一区二区| 欧美一区二区三区另类 | 已婚少妇美妙人妻系列| 亚洲激情播播| 日本精品视频网站| 国产精品一二三区视频| 色哟哟一区二区在线观看| 欧美无人区码suv| 噜噜噜在线观看免费视频日韩| 另类小说综合网| 欧美日韩成人影院| 色偷偷偷亚洲综合网另类 | 亚洲成人激情在线观看| 激情综合网五月婷婷| 成人午夜视频在线观看| 黄网站欧美内射| 色天下一区二区三区| 欧美在线视频播放| 成年人视频网站在线| 欧美无乱码久久久免费午夜一区 | 538任你躁在线精品免费| 精品香蕉视频| 国产日韩欧美影视| 性欧美猛交videos| 亚洲精品电影网在线观看| 国产无遮挡又黄又爽在线观看| 9久草视频在线视频精品| 日本福利视频在线| 精品国产91久久久久久浪潮蜜月| 国产欧美最新羞羞视频在线观看| 国产网站在线免费观看| 日韩欧美国产一区二区三区| 日产亚洲一区二区三区| 久久精品视频一区| 午夜天堂在线视频| 99亚洲一区二区| 亚洲电影网站| aaa国产精品| 国产精品对白刺激| 中文在线观看免费| 亚洲毛片在线免费观看| 91禁在线观看| 黄色成人在线播放| 国产主播av在线| 粉嫩嫩av羞羞动漫久久久| 欧美日韩亚洲第一| 亚洲精品888| 欧美一级二级三级九九九| 国产精久久一区二区| 66m—66摸成人免费视频| 日本免费视频在线观看| 亚洲国产精品字幕| 自拍偷拍第八页| 午夜精品一区在线观看| 五月婷婷综合激情网| 99久久99久久综合| 国产xxxxhd| 日韩国产高清在线| 欧美久久久久久久久久久久久| 日韩欧美视频在线播放| 久久99国产精品| 日韩一区免费| 国产日韩一区在线| 成年美女黄网站色大片不卡| 欧美福利视频网站| 里番在线观看网站| 亚洲激情视频网| www.天堂av.com| 欧美精品一二三四| 黄色大全在线观看| 欧美日韩国产专区| 国产一级片免费看| 亚洲欧洲制服丝袜| 正在播放国产对白害羞| 91老师片黄在线观看| 日本一级大毛片a一| 久久精品国产秦先生| 美女福利视频在线| 999在线观看精品免费不卡网站| 五月天av影院| 欧美hd在线| 视频在线一区二区三区| 杨幂一区二区三区免费看视频| 国产一区国产精品| 好吊妞视频这里有精品| 97人摸人人澡人人人超一碰| www.成人在线.com| 成人国产在线视频| 日日狠狠久久| 成人春色激情网| 四虎影视精品永久在线观看| 国产裸体写真av一区二区| 欧美成人一二区| 国产精品一区久久| 国产精品传媒麻豆hd| 国产999精品久久久| 亚洲妇女成熟| 欧美有码在线观看| 欧美日韩美女| 国产脚交av在线一区二区| 小黄鸭精品aⅴ导航网站入口| 26uuu久久噜噜噜噜| 亚洲性色av| 日本亚洲欧洲色α| 日韩av一级| 国产欧美一区二区三区视频| 久久夜夜久久| 亚洲最大的av网站| 亚洲精品一区二区三区在线| 俄罗斯精品一区二区三区| 中文久久电影小说| 国产在线精品一区二区三区| 色老板在线视频一区二区| 日本一区二区三区四区在线观看| 精品国产一区二区三区久久久樱花 | 日韩欧美在线观看免费| 在线看国产一区二区| 亚洲天堂视频在线| 欧美一区二区三区的| 丰满熟妇人妻中文字幕| 国产网站欧美日韩免费精品在线观看| 黄色在线视频观看网站| 日韩有码片在线观看| 欧美xxxx黑人又粗又长| 88国产精品欧美一区二区三区| 日韩大片欧美大片| 国产日韩中文字幕| 在线观看视频一区二区三区 | 久久免费av| 伊人再见免费在线观看高清版 | 女优一区二区三区| 四虎一区二区| 欧美黄色免费| av动漫在线观看| 久久99在线观看| 捆绑裸体绳奴bdsm亚洲| 欧美韩国日本综合| 久久久夜色精品| 欧洲精品一区二区| 亚洲第一第二区| 亚洲色图狂野欧美| 性xxxxfjsxxxxx欧美| 日本精品久久久| 精品国产亚洲日本| 欧美日韩亚洲免费| 国产精品vip| 五月婷婷六月合| av午夜精品一区二区三区| 精品熟妇无码av免费久久| 一级女性全黄久久生活片免费| 亚洲第一网站在线观看| 91精品国产欧美日韩| 日韩专区一区二区| 九九精品在线观看| 精品亚洲美女网站| 国产伦精品一区二区三区视频黑人| 日本不卡电影| 国产精品12345| 国产精品综合一区二区| 69精品无码成人久久久久久| 午夜精品爽啪视频| 精品毛片一区二区三区| 在线播放国产一区二区三区| 17videosex性欧美| 亚洲淫片在线视频| 日本久久精品| 日韩人妻精品无码一区二区三区| 国产做a爰片久久毛片| 成人在线一级片| 亚洲成人激情综合网| 国产ts人妖调教重口男| 一道本无吗dⅴd在线播放一区 | 天堂成人在线| 色综合男人天堂| 国产95亚洲| 在线观看一区二区三区三州| 久色成人在线| 泷泽萝拉在线播放| 性做久久久久久久免费看| 国产女人高潮毛片| 日韩在线观看免费| 欧美一级在线| 一本一本a久久| 日韩国产欧美在线播放| 性少妇bbw张开| 欧美丝袜美女中出在线| 丰满少妇在线观看bd| 欧美高跟鞋交xxxxxhd| 欧美精品三级在线| 黄瓜视频免费观看在线观看www| 日本sm残虐另类| 国产一区二区三区四区五区六区 | 欧美精品做受xxx性少妇| 日韩午夜视频在线| 亚洲国产欧美不卡在线观看| 日本系列欧美系列| 欧美精品日韩在线| 欧美三级乱人伦电影| 69久久久久| 91精品在线观| 91精品成人| 丰满人妻一区二区三区大胸| 艳妇臀荡乳欲伦亚洲一区| 亚洲精品久久久久久动漫器材一区| 九九热精品在线| 成人高潮a毛片免费观看网站| 91亚洲精品国产| av电影在线观看一区| 69视频免费在线观看| 亚洲人成网7777777国产| 伊人久久高清| 一区二区不卡在线观看| 国产一区美女在线| 九九九国产视频| 亚洲人成网站999久久久综合| 中文.日本.精品| 性欧美18一19内谢| 国产ts人妖一区二区| 在线观看精品国产| 国产午夜精品免费一区二区三区 | 午夜在线播放视频欧美| 久久久久久久久久久久| 欧美日韩免费视频| 性欧美video高清bbw| 鲁丝一区鲁丝二区鲁丝三区| 麻豆精品一区二区综合av| 久久免费看少妇高潮v片特黄| 精品国产一二三| 毛片无码国产| 中文字幕一区二区三区在线乱码| 成人性生交大片免费看中文| 亚洲欧美自拍视频| 色阁综合伊人av| 日韩成人久久| 国产欧美在线一区| 一区视频在线播放| 性xxxx视频| 国产日韩欧美在线播放| 亚洲免费观看| 中文字幕在线观看二区| 欧美刺激午夜性久久久久久久| 日韩深夜视频| 午夜探花在线观看| ww亚洲ww在线观看国产| 99久久久久成人国产免费| 91精品国产免费久久久久久| 97精品视频在线看| 欧美丰满少妇人妻精品| 欧美一区二区三区在线观看视频|