Английский документ
Spring-boot-starter для модернизации, поддерживающий быструю интеграцию и расширение функций .
Проект продолжает оптимизироваться и повторяться, и каждый может присылать ПРОБЛЕМЫ и PR! Пожалуйста, поставьте нам звезду. Ваша звезда — наша мотивация для постоянных обновлений!
Адрес проекта Github: https://github.com/LianjiaTech/retrofit-spring-boot-starter.
адрес проекта gitee: https://gitee.com/lianjiatech/retrofit-spring-boot-starter
Пример демонстрации: https://github.com/ismart-yuxi/retrofit-spring-boot-demo.
Спасибо
@ismart-yuxi
за написание примера демо-версии для этого проекта.
< dependency >
< groupId >com.github.lianjiatech</ groupId >
< artifactId >retrofit-spring-boot-starter</ artifactId >
< version >3.1.3</ version >
</ dependency >
Если запуск не удался, высока вероятность возникновения конфликта зависимостей. Введите или исключите связанные зависимости .
Интерфейс должен быть помечен аннотацией @RetrofitClient
! Информацию, связанную с HTTP, см. в официальной документации: официальная документация по модификации.
@ RetrofitClient ( baseUrl = "${test.baseUrl}" )
public interface UserService {
/**
* 根据id查询用户姓名
*/
@ POST ( "getName" )
String getName ( @ Query ( "id" ) Long id );
}
Примечание. Пути запроса метода должны начинаться с
/
с осторожностью . ДляRetrofit
, еслиbaseUrl=http://localhost:8080/api/test/
и путь запроса метода —person
, полный путь запроса метода:http://localhost:8080/api/test/person
. Если путь запроса метода —/person
, полный путь запроса метода:http://localhost:8080/person
.
Внедрите интерфейс в другие Сервисы и используйте его!
@ Service
public class BusinessService {
@ Autowired
private UserService userService ;
public void doBusiness () {
// call userService
}
}
По умолчанию для регистрации RetrofitClient
автоматически используется путь сканирования SpringBoot
. Вы также можете добавить @RetrofitScan
в класс конфигурации, чтобы вручную указать путь сканирования.
Все аннотации, связанные с HTTP
запросами, используют собственные аннотации Retrofit
. Ниже приводится простое объяснение:
Классификация аннотаций | Поддерживаемые аннотации |
---|---|
Метод запроса | @GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP |
Заголовок запроса | @Header @HeaderMap @Headers |
Параметры запроса | @Query @QueryMap @QueryName |
параметр пути | @Path |
параметры, закодированные в форме | @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
# 是否聚合打印请求日志
aggregate : true
# 全局重试配置
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
. Для этого выполните следующие действия:
Реализуйте интерфейс SourceOkHttpClientRegistrar
и вызовите метод SourceOkHttpClientRegistry#register()
для регистрации OkHttpClient
@ Component
public class CustomOkHttpClientRegistrar implements SourceOkHttpClientRegistrar {
@ Override
public void register ( SourceOkHttpClientRegistry registry ) {
// 注册customOkHttpClient,超时时间设置为1s
registry . register ( "customOkHttpClient" , new OkHttpClient . Builder ()
. connectTimeout ( Duration . ofSeconds ( 1 ))
. writeTimeout ( Duration . ofSeconds ( 1 ))
. readTimeout ( Duration . ofSeconds ( 1 ))
. addInterceptor ( chain -> chain . proceed ( chain . request ()))
. build ());
}
}
Укажите OkHttpClient
, который будет использоваться текущим интерфейсом, через @RetrofitClient.sourceOkHttpClient
@ RetrofitClient ( baseUrl = "${test.baseUrl}" , sourceOkHttpClient = "customOkHttpClient" )
public interface CustomOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Примечание. Компонент не будет использовать указанный
OkHttpClient
напрямую, а создаст новый на основеOkHttpClient
.
Компонент предоставляет перехватчик аннотаций , который поддерживает перехват на основе сопоставления URL-адресов. Для этого необходимо выполнить следующие шаги:
BasePathMatchInterceptor
@Intercept
, чтобы указать используемый перехватчик.Если вам нужно использовать несколько перехватчиков, просто отметьте несколько аннотаций
@Intercept
в интерфейсе.
BasePathMatchInterceptor
для записи процессора перехвата @ Component
public class PathMatchInterceptor extends BasePathMatchInterceptor {
@ Override
protected Response doIntercept ( Chain chain ) throws IOException {
Response response = chain . proceed ( chain . request ());
// response的Header加上path.match
return response . newBuilder (). header ( "path.match" , "true" ). build ();
}
}
По умолчанию компонент автоматически устанавливает scope
BasePathMatchInterceptor
в prototype
. Эту функцию можно отключить с помощью retrofit.auto-set-prototype-scope-for-path-math-interceptor=false
. После закрытия вам необходимо вручную установить scope
prototype
.
@ Component
@ Scope ( "prototype" )
public class PathMatchInterceptor extends BasePathMatchInterceptor {
}
@Intercept
для аннотирования интерфейса. @ RetrofitClient ( baseUrl = "${test.baseUrl}" )
@ Intercept ( handler = PathMatchInterceptor . class , include = { "/api/user/**" }, exclude = "/api/user/getUser" )
// @Intercept() 如果需要使用多个路径匹配拦截器,继续添加@Intercept即可
public interface InterceptorUserService {
/**
* 根据id查询用户姓名
*/
@ POST ( "getName" )
Response < String > getName ( @ Query ( "id" ) Long id );
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
Response < User > getUser ( @ Query ( "id" ) Long id );
}
Приведенная выше конфигурация @Intercept
означает: перехват запросов по пути /api/user/**
в интерфейсе InterceptorUserService
(исключая /api/user/getUser
), а процессор перехвата использует PathMatchInterceptor
.
Иногда нам нужно динамически передавать некоторые параметры в «аннотации перехвата», а затем использовать эти параметры при перехвате. В настоящее время мы можем использовать «пользовательские аннотации перехвата». Шаги следующие:
@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 ;
}
Используемый перехватчик — SignInterceptor
указанный в аннотации @Sign
.
SignInterceptor
@ Component
@ Setter
public class SignInterceptor extends BasePathMatchInterceptor {
private String accessKeyId ;
private String accessKeySecret ;
@ Override
public Response doIntercept ( Chain chain ) throws IOException {
Request request = chain . request ();
Request newReq = request . newBuilder ()
. addHeader ( "accessKeyId" , accessKeyId )
. addHeader ( "accessKeySecret" , accessKeySecret )
. build ();
Response response = chain . proceed ( newReq );
return response . newBuilder (). addHeader ( "accessKeyId" , accessKeyId )
. addHeader ( "accessKeySecret" , accessKeySecret ). build ();
}
}
Примечание. Поля
accessKeyId
иaccessKeySecret
должны содержать методыsetter
.
Значения полей accessKeyId
и accessKeySecret
перехватчика будут автоматически вставлены на основе значений accessKeyId()
и accessKeySecret()
аннотации @Sign
. Если @Sign
указывает строку в форме заполнителя, то Значение атрибута конфигурации будет использоваться для внедрения.
@Sign
в интерфейсе @ RetrofitClient ( baseUrl = "${test.baseUrl}" )
@ Sign ( accessKeyId = "${test.accessKeyId}" , accessKeySecret = "${test.accessKeySecret}" , include = "/api/user/getAll" )
public interface InterceptorUserService {
/**
* 查询所有用户信息
*/
@ GET ( "getAll" )
Response < List < User >> getAll ();
}
Компонент поддерживает глобальную и декларативную печать журналов.
По умолчанию печать глобального журнала включена, а конфигурация по умолчанию следующая:
retrofit :
# 全局日志打印配置
global-log :
# 启用日志打印
enable : true
# 全局日志打印级别
log-level : info
# 全局日志打印策略
log-strategy : basic
# 是否聚合打印请求日志
aggregate : true
# 日志名称,默认为{@link LoggingInterceptor} 的全类名
logName : com.github.lianjiatech.retrofit.spring.boot.log.LoggingInterceptor
Значения четырех стратегий печати журналов следующие:
NONE
: журналов нет.BASIC
: регистрирует строки запроса и ответа.HEADERS
: записывает строки запроса и ответа, а также соответствующие заголовки.BODY
: регистрирует строки запроса и ответа, а также соответствующие заголовки и тела (если они присутствуют). Если вам нужно распечатать журналы только для некоторых запросов, вы можете использовать аннотацию @Logging
для соответствующего интерфейса или метода.
Если вам нужно изменить поведение печати журналов, вы можете наследовать LoggingInterceptor
и настроить его как Spring bean
.
Поддержка компонентов поддерживает глобальную и декларативную повторную попытку.
По умолчанию глобальная повторная попытка отключена, а элементы конфигурации по умолчанию следующие:
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
: Повторить попытку при возникновении исключения ввода-вывода.OCCUR_EXCEPTION
: Повторить попытку при возникновении какого-либо исключения. Если необходимо повторить только некоторые запросы, вы можете использовать аннотацию @Retry
для соответствующего интерфейса или метода.
Если вам нужно изменить поведение повтора запроса, вы можете наследовать RetryInterceptor
и настроить его как Spring bean
.
Понижение версии автоматического выключателя отключено по умолчанию и в настоящее время поддерживает реализации sentinel
и resilience4j
.
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : 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全局配置
Настройте 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
Управление конфигурацией выключателя:
Реализуйте интерфейс CircuitBreakerConfigRegistrar
и зарегистрируйте CircuitBreakerConfig
.
@ Component
public class 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 ());
}
}
CircuitBreakerConfig
указывается в circuitBreakerConfigName
. Включите retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name
или @Resilience4jDegrade.circuitBreakerConfigName
Если пользователю необходимо использовать другую реализацию деградации автоматического выключателя, унаследуйте BaseRetrofitDegrade
и настройте ее Spring Bean
.
Если @RetrofitClient
не устанавливает fallback
или fallbackFactory
, при срабатывании автоматического выключателя RetrofitBlockException
будет создано напрямую. Пользователи могут настроить возвращаемое значение метода при слиянии, установив fallback
или fallbackFactory
.
Примечание.
fallback
класс должен быть классом реализации текущего интерфейса,fallbackFactory
должен быть классом реализацииFallbackFactory<T>
, а тип универсального параметра должен быть текущим типом интерфейса. Кроме того, экземплярыfallback
иfallbackFactory
должны быть настроены какSpring Bean
.
По сравнению с fallbackFactory
fallback
заключается в том, что он может определять аномальную причину (причину) каждого автоматического выключателя. Справочный пример выглядит следующим образом:
@ Slf4j
@ Service
public class HttpDegradeFallback implements HttpDegradeApi {
@ Override
public Result < Integer > test () {
Result < Integer > fallback = new Result <>();
fallback . setCode ( 100 )
. setMsg ( "fallback" )
. setBody ( 1000000 );
return fallback ;
}
}
@ Slf4j
@ Service
public class HttpDegradeFallbackFactory implements FallbackFactory < HttpDegradeApi > {
@ Override
public HttpDegradeApi create ( Throwable cause ) {
log . error ( "触发熔断了! " , cause . getMessage (), cause );
return new HttpDegradeApi () {
@ Override
public Result < Integer > test () {
Result < Integer > fallback = new Result <>();
fallback . setCode ( 100 )
. setMsg ( "fallback" )
. setBody ( 1000000 );
return fallback ;
}
};
}
}
При возникновении ошибки HTTP
запроса (включая исключение или данные ответа не соответствуют ожиданиям) декодер ошибок может декодировать информацию, связанную с HTTP
в пользовательское исключение. Вы можете указать декодер ошибок текущего интерфейса в аннотации errorDecoder errorDecoder()
@RetrofitClient
. Пользовательский декодер ошибок должен реализовать интерфейс ErrorDecoder
:
ServiceInstanceChooser
Пользователи могут самостоятельно реализовать интерфейс ServiceInstanceChooser
, завершить логику выбора экземпляра службы и настроить его как Spring Bean
. Для приложений Spring Cloud
можно использовать следующую реализацию.
@ Service
public class 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 = "user" , path = "/api/user" )
public interface ChooserOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Если нам нужно выполнить унифицированную обработку перехвата HTTP
запросов всей системы, мы можем реализовать глобальный перехватчик GlobalInterceptor
и настроить его как spring Bean
.
@ Component
public class MyGlobalInterceptor implements GlobalInterceptor {
@ Override
public Response intercept ( Chain chain ) throws IOException {
Response response = chain . proceed ( chain . request ());
// response的Header加上global
return response . newBuilder (). header ( "global" , "true" ). build ();
}
}
Реализуйте интерфейс NetworkInterceptor
и настройте его как spring Bean
.
Retrofit
может адаптировать объект Call<T>
к типу возвращаемого значения метода интерфейса с помощью CallAdapterFactory
. Компонент расширяет некоторые реализации CallAdapterFactory
:
BodyCallAdapterFactory
HTTP
запросы синхронно и адаптируйте содержимое тела ответа к типу возвращаемого значения метода.BodyCallAdapterFactory
с наименьшим приоритетом.ResponseCallAdapterFactory
HTTP
запрос синхронно, адаптируйте содержимое тела ответа к Retrofit.Response<T>
и верните его.ResponseCallAdapterFactory
можно использовать только в том случае, если тип возвращаемого значения метода — Retrofit.Response<T>
.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
). CallAdapter
можно расширить, унаследовав CallAdapter.Factory
.
Компонент поддерживает фабрики адаптеров глобальных вызовов посредством конфигурации retrofit.global-call-adapter-factories
:
retrofit :
# 全局转换器工厂(组件扩展的`CallAdaptorFactory`工厂已经内置,这里请勿重复配置)
global-call-adapter-factories :
# ...
Для каждого интерфейса Java вы также можете указать CallAdapter.Factory
используемый текущим интерфейсом, через @RetrofitClient.callAdapterFactories
.
Предложение: настроить
CallAdapter.Factory
какSpring Bean
Retrofit
использует Converter
для преобразования аннотированного объекта @Body
в Request Body
и преобразования Response Body
в объект Java
. Вы можете выбрать один из следующих Converter
:
Компонент поддерживает настройку глобального Converter.Factory
с помощью retrofit.global-converter-factories
. По умолчанию используется retrofit2.converter.jackson.JacksonConverterFactory
.
Если вам нужно изменить конфигурацию Jackson
, просто перезапишите конфигурацию bean
JacksonConverterFactory
самостоятельно.
retrofit :
# 全局转换器工厂
global-converter-factories :
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
Для каждого интерфейса Java
вы также можете указать Converter.Factory
используемый текущим интерфейсом, через @RetrofitClient.converterFactories
.
Предложение: настроить
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 ;
}
@ 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 );
// 对文件名使用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://img.ljcdn.com/hc-picture/" )
public interface DownloadApi {
@ GET ( "{fileKey}" )
Response < ResponseBody > download ( @ Path ( "fileKey" ) String fileKey );
}
HTTP
загрузки @ SpringBootTest ( classes = { RetrofitBootApplication . 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 = new byte [ 1024 ];
int length ;
while (( length = is . read ( b )) > 0 ) {
fos . write ( b , 0 , length );
}
is . close ();
fos . close ();
}
}
Динамические 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
запросы. Исходный код выглядит следующим образом:
Автор привел конкретные причины, вы можете обратиться к: проблема
Однако, если вам действительно нужно это сделать, вы можете использовать: @HTTP(method = "get", path = "/user/get", hasBody = true)
и использовать строчные буквы get
, чтобы обойти вышеуказанные ограничения.
Если у вас есть какие-либо вопросы, поднимите проблему или присоединитесь к группе QQ для получения обратной связи.
Номер группы: 806714302