Documento Inglês
Spring-boot-starter para retrofit, suportando integração rápida e aprimoramento de recursos .
O projeto continua a ser otimizado e iterado, e todos são bem-vindos para enviar PROBLEMAS e PRs! Por favor, dê-nos uma estrela. Sua estrela é nossa motivação para atualizações contínuas!
Endereço do projeto Github: https://github.com/LianjiaTech/retrofit-spring-boot-starter
endereço do projeto gitee: https://gitee.com/lianjiatech/retrofit-spring-boot-starter
Exemplo de demonstração: https://github.com/ismart-yuxi/retrofit-spring-boot-demo
Obrigado a
@ismart-yuxi
por escrever o exemplo de demonstração deste projeto
< dependency >
< groupId >com.github.lianjiatech</ groupId >
< artifactId >retrofit-spring-boot-starter</ artifactId >
< version >3.1.3</ version >
</ dependency >
Se a inicialização falhar, há uma grande probabilidade de haver um conflito de dependência. Por favor, introduza ou exclua dependências relacionadas .
A interface deve ser marcada com @RetrofitClient
! Para obter informações relacionadas ao HTTP, consulte a documentação oficial: documentação oficial de retrofit.
@ RetrofitClient ( baseUrl = "${test.baseUrl}" )
public interface UserService {
/**
* 根据id查询用户姓名
*/
@ POST ( "getName" )
String getName ( @ Query ( "id" ) Long id );
}
Nota: Os caminhos de solicitação de método devem começar com
/
com cuidado . ParaRetrofit
, sebaseUrl=http://localhost:8080/api/test/
e o caminho da solicitação do método forperson
, o caminho completo da solicitação do método será:http://localhost:8080/api/test/person
. Se o caminho de solicitação do método for/person
, o caminho de solicitação completo do método será:http://localhost:8080/person
.
Injete a interface em outros serviços e use-a!
@ Service
public class BusinessService {
@ Autowired
private UserService userService ;
public void doBusiness () {
// call userService
}
}
Por padrão, o caminho de verificação SpringBoot
é usado automaticamente para registro RetrofitClient
. Você também pode adicionar @RetrofitScan
à classe de configuração para especificar manualmente o caminho de verificação.
Todas as anotações relacionadas a solicitações HTTP
usam anotações nativas Retrofit
. A seguir está uma explicação simples:
Classificação de anotação | Anotações suportadas |
---|---|
Método de solicitação | @GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP |
Cabeçalho da solicitação | @Header @HeaderMap @Headers |
Parâmetros de consulta | @Query @QueryMap @QueryName |
parâmetro de caminho | @Path |
parâmetros codificados em formulário | @Field @FieldMap @FormUrlEncoded |
Solicitar corpo | @Body |
Carregamento de arquivo | @Multipart @Part @PartMap |
parâmetros de URL | @Url |
Para obter informações detalhadas, consulte a documentação oficial: documentação oficial de retrofit
O componente suporta vários atributos configuráveis para lidar com diferentes cenários de negócios. Os atributos de configuração específicos suportados e os valores padrão são os seguintes:
Nota: O aplicativo só precisa configurar os itens de configuração a serem alterados !
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
Se você precisar apenas modificar o tempo limite de OkHttpClient
, poderá modificá-lo por meio dos campos relacionados @RetrofitClient
ou modificar a configuração de tempo limite global.
Se precisar modificar outras configurações OkHttpClient
, você pode fazer isso personalizando OkHttpClient
.
Implemente a interface SourceOkHttpClientRegistrar
e chame SourceOkHttpClientRegistry#register()
para registrar 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 ());
}
}
Especifique OkHttpClient
a ser usado pela interface atual por meio de @RetrofitClient.sourceOkHttpClient
@ RetrofitClient ( baseUrl = "${test.baseUrl}" , sourceOkHttpClient = "customOkHttpClient" )
public interface CustomOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Nota: O componente não usará o
OkHttpClient
especificado diretamente, mas criará um novo baseado noOkHttpClient
.
O componente fornece um interceptador de anotação que suporta interceptação com base na correspondência de caminho de URL. As etapas a serem usadas são as seguintes:
BasePathMatchInterceptor
@Intercept
para especificar o interceptor a ser usadoSe você precisar usar vários interceptadores, basta marcar várias anotações
@Intercept
na interface.
BasePathMatchInterceptor
para gravar o processador de interceptação @ 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 ();
}
}
Por padrão, o componente define automaticamente scope
BasePathMatchInterceptor
como prototype
. Este recurso pode ser desativado via retrofit.auto-set-prototype-scope-for-path-math-interceptor=false
. Após fechar, você precisa definir manualmente scope
para prototype
.
@ Component
@ Scope ( "prototype" )
public class PathMatchInterceptor extends BasePathMatchInterceptor {
}
@Intercept
para anotar a interface @ 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 );
}
A configuração @Intercept
acima significa: interceptar solicitações no caminho /api/user/**
na interface InterceptorUserService
(excluindo /api/user/getUser
) e o processador de interceptação usa PathMatchInterceptor
.
Às vezes, precisamos passar dinamicamente alguns parâmetros na "anotação de interceptação" e então usar esses parâmetros ao interceptar. Neste momento, podemos usar "anotações de interceptação personalizadas". As etapas são as seguintes:
@InterceptMark
deve ser usada e a anotação deve incluir campos include、exclude、handler
.BasePathMatchInterceptor
para gravar o processador de interceptação Por exemplo, precisamos "adicionar dinamicamente informações de assinatura accessKeyId
e accessKeySecret
ao cabeçalho da solicitação antes de iniciar uma solicitação HTTP. Nesse caso, podemos personalizar @Sign
para conseguir isso".
@Sign
personalizada @ 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 ;
}
O interceptor utilizado é SignInterceptor
especificado na anotação @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 ();
}
}
Nota: Os campos
accessKeyId
eaccessKeySecret
devem fornecer métodossetter
.
accessKeyId
e accessKeySecret
do interceptor serão injetados automaticamente com base accessKeyId()
e accessKeySecret()
da anotação @Sign
. Se @Sign
especificar uma string na forma de um espaço reservado, o valor será adicionado. o valor do atributo de configuração será usado para injeção.
@Sign
na interface @ RetrofitClient ( baseUrl = "${test.baseUrl}" )
@ Sign ( accessKeyId = "${test.accessKeyId}" , accessKeySecret = "${test.accessKeySecret}" , include = "/api/user/getAll" )
public interface InterceptorUserService {
/**
* 查询所有用户信息
*/
@ GET ( "getAll" )
Response < List < User >> getAll ();
}
O componente suporta impressão de log global e impressão de log declarativo.
Por padrão, a impressão de log global está habilitada e a configuração padrão é a seguinte:
retrofit :
# 全局日志打印配置
global-log :
# 启用日志打印
enable : true
# 全局日志打印级别
log-level : info
# 全局日志打印策略
log-strategy : basic
# 是否聚合打印请求日志
aggregate : true
# 日志名称,默认为{@link LoggingInterceptor} 的全类名
logName : com.github.lianjiatech.retrofit.spring.boot.log.LoggingInterceptor
Os significados das quatro estratégias de impressão de toras são os seguintes:
NONE
: Sem registros.BASIC
: Registra linhas de solicitação e resposta.HEADERS
: Registra linhas de solicitação e resposta e seus respectivos cabeçalhos.BODY
: Registra linhas de solicitação e resposta e seus respectivos cabeçalhos e corpos (se presentes). Se você precisar imprimir logs apenas para algumas solicitações, poderá usar a anotação @Logging
na interface ou método relevante.
Se precisar modificar o comportamento de impressão do log, você pode herdar LoggingInterceptor
e configurá-lo como Spring bean
.
O suporte a componentes dá suporte à nova tentativa global e à nova tentativa declarativa.
A nova tentativa global está desativada por padrão e os itens de configuração padrão são os seguintes:
retrofit :
# 全局重试配置
global-retry :
# 是否启用全局重试
enable : false
# 全局重试间隔时间
interval-ms : 100
# 全局最大重试次数
max-retries : 2
# 全局重试规则
retry-rules :
- response_status_not_2xx
- occur_io_exception
As regras de nova tentativa suportam três configurações:
RESPONSE_STATUS_NOT_2XX
: tente novamente quando o código de status da resposta não for 2xx
OCCUR_IO_EXCEPTION
: Tente novamente quando ocorrer uma exceção de IOOCCUR_EXCEPTION
: Tente novamente quando ocorrer alguma exceção Se apenas algumas solicitações precisarem ser repetidas, você poderá usar a anotação @Retry
na interface ou método correspondente.
Se precisar modificar o comportamento de nova tentativa de solicitação, você pode herdar RetryInterceptor
e configurá-lo como Spring bean
.
O downgrade do disjuntor está desativado por padrão e atualmente oferece suporte a implementações sentinel
e resilience4j
.
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : sentinel
Configure degrade-type=sentinel
para habilitá-lo e, em seguida, declare a anotação @SentinelDegrade
na interface ou método relevante.
Lembre-se de introduzir manualmente as dependências Sentinel
:
< dependency >
< groupId >com.alibaba.csp</ groupId >
< artifactId >sentinel-core</ artifactId >
< version >1.6.3</ version >
</ dependency >
Além disso, o downgrade global do disjuntor Sentinel
também é suportado:
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : sentinel
# 全局sentinel降级配置
global-sentinel-degrade :
# 是否开启
enable : true
# ...其他sentinel全局配置
Configure degrade-type=resilience4j
para habilitá-lo. Em seguida, declare @Resilience4jDegrade
na interface ou método relevante.
Lembre-se de introduzir manualmente Resilience4j
:
< dependency >
< groupId >io.github.resilience4j</ groupId >
< artifactId >resilience4j-circuitbreaker</ artifactId >
< version >1.7.1</ version >
</ dependency >
O downgrade do disjuntor de resiliência global4j pode ser habilitado por meio da seguinte configuração:
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : resilience4j
# 全局resilience4j降级配置
global-resilience4j-degrade :
# 是否开启
enable : true
# 根据该名称从#{@link CircuitBreakerConfigRegistry}获取CircuitBreakerConfig,作为全局熔断配置
circuit-breaker-config-name : defaultCircuitBreakerConfig
Gerenciamento de configuração do disjuntor:
Implemente a interface CircuitBreakerConfigRegistrar
e registre 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
é especificado por circuitBreakerConfigName
. Inclui retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name
ou @Resilience4jDegrade.circuitBreakerConfigName
Caso o usuário precise utilizar outra implementação de degradação do disjuntor, herde BaseRetrofitDegrade
e configure-o Spring Bean
.
Se @RetrofitClient
não definir fallback
ou fallbackFactory
, quando o disjuntor for acionado, RetrofitBlockException
será lançado diretamente. Os usuários podem personalizar o valor de retorno do método quando ocorre a fusão, definindo fallback
ou fallbackFactory
.
Nota: A classe
fallback
deve ser a classe de implementação da interface atual,fallbackFactory
deve serFallbackFactory<T>
e o tipo de parâmetro genérico deve ser o tipo de interface atual. Além disso, as instânciasfallback
efallbackFactory
devem ser configuradas comoSpring Bean
.
Comparado com fallbackFactory
fallback
é que ele pode detectar a causa anormal (causa) de cada disjuntor. O exemplo de referência é o seguinte:
@ 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 ;
}
};
}
}
Quando ocorre HTTP
(incluindo uma exceção ou os dados de resposta não atendem às expectativas), o decodificador de erro pode decodificar informações relacionadas HTTP
em uma exceção personalizada. Você pode especificar o decodificador de erro da interface atual na anotação errorDecoder errorDecoder()
de @RetrofitClient
. Um decodificador de erro personalizado precisa implementar a interface ErrorDecoder
:
ServiceInstanceChooser
Os usuários podem implementar a interface ServiceInstanceChooser
sozinhos, completar a lógica de seleção da instância de serviço e configurá-la como Spring Bean
. Para aplicativos Spring Cloud
, a implementação a seguir pode ser usada.
@ 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
e path
@ RetrofitClient ( serviceId = "user" , path = "/api/user" )
public interface ChooserOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Se precisarmos realizar um processamento unificado de interceptação nas solicitações HTTP
de todo o sistema, podemos implementar o interceptor global GlobalInterceptor
e configurá-lo como 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 ();
}
}
Implemente a interface NetworkInterceptor
e configure-a como spring Bean
.
Retrofit
pode adaptar o objeto Call<T>
ao tipo de valor de retorno do método de interface por meio de CallAdapterFactory
. O componente estende algumas implementações CallAdapterFactory
:
BodyCallAdapterFactory
HTTP
de forma síncrona e adapte o conteúdo do corpo da resposta ao tipo de valor de retorno do método.BodyCallAdapterFactory
, com a prioridade mais baixa.ResponseCallAdapterFactory
HTTP
de forma síncrona, adapte o conteúdo do corpo da resposta para Retrofit.Response<T>
e retorne-o.ResponseCallAdapterFactory
poderá ser usado somente se o tipo de valor de retorno do método for Retrofit.Response<T>
.CallAdapterFactory
relacionado à programação reativa Retrofit
selecionará o CallAdapterFactory
correspondente para realizar o processamento de adaptação com base no tipo de valor de retorno do método . Os tipos de valor de retorno atualmente suportados são os seguintes:
String
: Adapte Response Body
para String
e retorne-o.Long
/ Integer
/ Boolean
/ Float
/ Double
): Adapte Response Body
aos tipos básicos acimaJava
: Adapte Response Body
ao objeto Java
correspondente e retorne-oCompletableFuture<T>
: Adapte Response Body
em CompletableFuture<T>
e retorne-oVoid
: Você pode usar Void
se não se importar com o tipo de retorno.Response<T>
: Adapte Response
no objeto Response<T>
e retorne-oCall<T>
: Não realiza processamento de adaptação e retorna diretamente o objeto Call<T>
Mono<T>
: tipo de retorno reativo Project Reactor
Single<T>
: tipo de retorno responsivo Rxjava
(suporta Rxjava2/Rxjava3
)Completable
: tipo de retorno responsivo Rxjava
, solicitação HTTP
não tem corpo de resposta (suporta Rxjava2/Rxjava3
) CallAdapter
pode ser estendido herdando CallAdapter.Factory
.
O componente oferece suporte a fábricas de adaptadores de chamadas globais por meio da configuração retrofit.global-call-adapter-factories
:
retrofit :
# 全局转换器工厂(组件扩展的`CallAdaptorFactory`工厂已经内置,这里请勿重复配置)
global-call-adapter-factories :
# ...
Para cada interface Java, você também pode especificar CallAdapter.Factory
usado pela interface atual por meio de @RetrofitClient.callAdapterFactories
.
Sugestão: Configure
CallAdapter.Factory
comoSpring Bean
Retrofit
usa Converter
para converter o objeto anotado @Body
em Request Body
e converter Response Body
em um objeto Java
. Você pode escolher entre os seguintes Converter
:
O componente suporta a configuração do Converter.Factory
global por meio de retrofit.global-converter-factories
. O padrão é retrofit2.converter.jackson.JacksonConverterFactory
.
Se você precisar modificar a configuração Jackson
, basta substituir você mesmo a configuração bean
do JacksonConverterFactory
.
retrofit :
# 全局转换器工厂
global-converter-factories :
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
Para cada interface Java
, você também pode especificar Converter.Factory
usado pela interface atual por meio de @RetrofitClient.converterFactories
.
Sugestão: Configure
Converter.Factory
comoSpring Bean
.
Anotações como @RetrofitClient
, @Retry
, @Logging
e @Resilience4jDegrade
suportam meta-anotações, herança e @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 ();
}
}
URLs dinâmicos podem ser implementados usando a anotação @url
. Neste momento, baseUrl
pode ser configurado com qualquer URL legal. Por exemplo: http://github.com/
. Em tempo de execução, as solicitações serão iniciadas apenas com base no endereço @Url
.
Nota:
@url
deve ser colocado na primeira posição dos parâmetros do método. Além disso,@GET
,@POST
e outras anotações não precisam definir o caminho do endpoint.
@ GET
Map < String , Object > test3 ( @ Url String url , @ Query ( "name" ) String name );
DELETE
adiciona o corpo da solicitação @ HTTP ( method = "DELETE" , path = "/user/delete" , hasBody = true )
GET
O próprio okhttp3
não suporta a adição de corpos de solicitação GET
. O código-fonte é o seguinte:
O autor deu razões específicas, você pode consultar: problema
No entanto, se você realmente precisar fazer isso, você pode usar: @HTTP(method = "get", path = "/user/get", hasBody = true)
e usar letras minúsculas get
para contornar as restrições acima.
Se você tiver alguma dúvida, levante um problema ou junte-se ao grupo QQ para obter feedback.
Número do grupo: 806714302