Documento en inglés
Spring-boot-starter para modernización, que admite una rápida integración y mejora de funciones .
El proyecto continúa optimizándose e iterándose, ¡y todos pueden enviar PROBLEMAS y PR! ¡Danos una estrella! ¡Tu estrella es nuestra motivación para actualizaciones continuas!
Dirección del proyecto Github: https://github.com/LianjiaTech/retrofit-spring-boot-starter
Dirección del proyecto gitee: https://gitee.com/lianjiatech/retrofit-spring-boot-starter
Demostración de muestra: https://github.com/ismart-yuxi/retrofit-spring-boot-demo
Gracias a
@ismart-yuxi
por escribir la demostración de muestra de este proyecto.
< dependency >
< groupId >com.github.lianjiatech</ groupId >
< artifactId >retrofit-spring-boot-starter</ artifactId >
< version >3.1.3</ version >
</ dependency >
Si el inicio falla, existe una alta probabilidad de que haya un conflicto de dependencias. Introduzca o excluya las dependencias relacionadas .
¡La interfaz debe estar marcada con @RetrofitClient
! Para obtener información relacionada con HTTP, consulte la documentación oficial: documentación oficial de actualización.
@ RetrofitClient ( baseUrl = "${test.baseUrl}" )
public interface UserService {
/**
* 根据id查询用户姓名
*/
@ POST ( "getName" )
String getName ( @ Query ( "id" ) Long id );
}
Nota: Las rutas de solicitud de métodos deben comenzar con
/
con precaución . ParaRetrofit
, sibaseUrl=http://localhost:8080/api/test/
y la ruta de solicitud del método esperson
, la ruta de solicitud completa del método es:http://localhost:8080/api/test/person
. Si la ruta de solicitud del método es/person
, la ruta de solicitud completa del método es:http://localhost:8080/person
.
¡Inyecte la interfaz en otros Servicios y úsela!
@ Service
public class BusinessService {
@ Autowired
private UserService userService ;
public void doBusiness () {
// call userService
}
}
De forma predeterminada, la ruta de escaneo SpringBoot
se usa automáticamente para el registro de RetrofitClient
. También puede agregar @RetrofitScan
a la clase de configuración para especificar manualmente la ruta de escaneo.
Todas las anotaciones relacionadas con solicitudes HTTP
utilizan anotaciones nativas Retrofit
. La siguiente es una explicación simple:
Clasificación de anotaciones | Anotaciones admitidas |
---|---|
Método de solicitud | @GET @HEAD @POST @PUT @DELETE @OPTIONS @HTTP |
Encabezado de solicitud | @Header @HeaderMap @Headers |
Parámetros de consulta | @Query @QueryMap @QueryName |
parámetro de ruta | @Path |
parámetros codificados en forma | @Field @FieldMap @FormUrlEncoded |
Cuerpo de la solicitud | @Body |
Carga de archivos | @Multipart @Part @PartMap |
parámetros de URL | @Url |
Para obtener información detallada, consulte la documentación oficial: documentación oficial de actualización
El componente admite múltiples atributos configurables para hacer frente a diferentes escenarios comerciales. Los atributos de configuración específicos admitidos y los valores predeterminados son los siguientes:
Nota: ¡La aplicación solo necesita configurar los elementos de configuración que se cambiarán !
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
Si solo necesita modificar el tiempo de espera de OkHttpClient
, puede modificarlo a través de los campos relacionados con @RetrofitClient
o modificar la configuración del tiempo de espera global.
Si necesita modificar otras configuraciones OkHttpClient
, puede hacerlo personalizando OkHttpClient
. Los pasos son los siguientes:
Implemente la interfaz SourceOkHttpClientRegistrar
y llame 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
que utilizará la interfaz actual a través de @RetrofitClient.sourceOkHttpClient
@ RetrofitClient ( baseUrl = "${test.baseUrl}" , sourceOkHttpClient = "customOkHttpClient" )
public interface CustomOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Nota: El componente no utilizará el
OkHttpClient
especificado directamente, sino que creará uno nuevo basado enOkHttpClient
.
El componente proporciona un interceptor de anotaciones que admite la interceptación basada en la coincidencia de rutas URL. Los pasos a utilizar son los siguientes:
BasePathMatchInterceptor
@Intercept
para especificar el interceptor a utilizarSi necesita utilizar varios interceptores, simplemente marque varias anotaciones
@Intercept
en la interfaz.
BasePathMatchInterceptor
para escribir el procesador de interceptación @ 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 ();
}
}
De forma predeterminada, el componente establece automáticamente scope
de BasePathMatchInterceptor
en prototype
. Esta función se puede desactivar mediante retrofit.auto-set-prototype-scope-for-path-math-interceptor=false
. Después de cerrar, debe configurar manualmente scope
en prototype
.
@ Component
@ Scope ( "prototype" )
public class PathMatchInterceptor extends BasePathMatchInterceptor {
}
@Intercept
para anotar la interfaz @ 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 );
}
La configuración @Intercept
anterior significa: interceptar solicitudes bajo la ruta /api/user/**
bajo la interfaz InterceptorUserService
(excluyendo /api/user/getUser
), y el procesador de interceptación usa PathMatchInterceptor
.
A veces, necesitamos pasar dinámicamente algunos parámetros en la "anotación de interceptación" y luego usar estos parámetros al interceptar. En este momento, podemos utilizar "anotaciones de interceptación personalizadas". Los pasos son los siguientes:
@InterceptMark
y la anotación debe incluir campos include、exclude、handler
.BasePathMatchInterceptor
para escribir el procesador de interceptación Por ejemplo, necesitamos "agregar dinámicamente información de firma accessKeyId
y accessKeySecret
al encabezado de la solicitud antes de iniciar una solicitud HTTP". En este caso, podemos personalizar @Sign
para lograr esto.
@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 ;
}
El interceptor utilizado es SignInterceptor
especificado en la anotación @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: Los campos
accessKeyId
yaccessKeySecret
deben proporcionar métodossetter
.
accessKeyId
y accessKeySecret
del interceptor se inyectarán automáticamente en función de accessKeyId()
y accessKeySecret()
de la anotación @Sign
. Si @Sign
especifica una cadena en forma de marcador de posición, el El valor del atributo de configuración se utilizará para la inyección.
@Sign
en la interfaz @ RetrofitClient ( baseUrl = "${test.baseUrl}" )
@ Sign ( accessKeyId = "${test.accessKeyId}" , accessKeySecret = "${test.accessKeySecret}" , include = "/api/user/getAll" )
public interface InterceptorUserService {
/**
* 查询所有用户信息
*/
@ GET ( "getAll" )
Response < List < User >> getAll ();
}
El componente admite la impresión de registros globales y la impresión de registros declarativos.
De forma predeterminada, la impresión de registros globales está habilitada y la configuración predeterminada es la siguiente:
retrofit :
# 全局日志打印配置
global-log :
# 启用日志打印
enable : true
# 全局日志打印级别
log-level : info
# 全局日志打印策略
log-strategy : basic
# 是否聚合打印请求日志
aggregate : true
# 日志名称,默认为{@link LoggingInterceptor} 的全类名
logName : com.github.lianjiatech.retrofit.spring.boot.log.LoggingInterceptor
Los significados de las cuatro estrategias de impresión de registros son los siguientes:
NONE
: Sin registros.BASIC
: Registra líneas de solicitud y respuesta.HEADERS
: Registra las líneas de solicitud y respuesta y sus respectivos encabezados.BODY
: Registra las líneas de solicitud y respuesta y sus respectivos encabezados y cuerpos (si están presentes). Si solo necesita imprimir registros para algunas solicitudes, puede usar la anotación @Logging
en la interfaz o método correspondiente.
Si necesita modificar el comportamiento de impresión de registros, puede heredar LoggingInterceptor
y configurarlo como Spring bean
.
El soporte de componentes admite el reintento global y el reintento declarativo.
El reintento global está desactivado de forma predeterminada y los elementos de configuración predeterminados son los siguientes:
retrofit :
# 全局重试配置
global-retry :
# 是否启用全局重试
enable : false
# 全局重试间隔时间
interval-ms : 100
# 全局最大重试次数
max-retries : 2
# 全局重试规则
retry-rules :
- response_status_not_2xx
- occur_io_exception
Las reglas de reintento admiten tres configuraciones:
RESPONSE_STATUS_NOT_2XX
: reintentar cuando el código de estado de respuesta no sea 2xx
OCCUR_IO_EXCEPTION
: reintentar cuando se produce una excepción IOOCCUR_EXCEPTION
: reintentar cuando se produzca alguna excepción Si solo es necesario volver a intentar algunas solicitudes, puede utilizar la anotación @Retry
en la interfaz o método correspondiente.
Si necesita modificar el comportamiento de reintento de la solicitud, puede heredar RetryInterceptor
y configurarlo como Spring bean
.
La degradación del disyuntor está desactivada de forma predeterminada y actualmente admite implementaciones sentinel
y resilience4j
.
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : sentinel
Configure degrade-type=sentinel
para habilitarlo y luego declare la anotación @SentinelDegrade
en la interfaz o método relevante.
Recuerde introducir manualmente las dependencias Sentinel
:
< dependency >
< groupId >com.alibaba.csp</ groupId >
< artifactId >sentinel-core</ artifactId >
< version >1.6.3</ version >
</ dependency >
Además, también se admite la degradación global del disyuntor Sentinel
:
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : sentinel
# 全局sentinel降级配置
global-sentinel-degrade :
# 是否开启
enable : true
# ...其他sentinel全局配置
Configure degrade-type=resilience4j
para habilitarlo. Luego declare @Resilience4jDegrade
en la interfaz o método relevante.
Recuerde introducir manualmente Resilience4j
:
< dependency >
< groupId >io.github.resilience4j</ groupId >
< artifactId >resilience4j-circuitbreaker</ artifactId >
< version >1.7.1</ version >
</ dependency >
La degradación del disyuntor global resilience4j se puede habilitar a través de la siguiente configuración:
retrofit :
# 熔断降级配置
degrade :
# 熔断降级类型。默认none,表示不启用熔断降级
degrade-type : resilience4j
# 全局resilience4j降级配置
global-resilience4j-degrade :
# 是否开启
enable : true
# 根据该名称从#{@link CircuitBreakerConfigRegistry}获取CircuitBreakerConfig,作为全局熔断配置
circuit-breaker-config-name : defaultCircuitBreakerConfig
Gestión de la configuración del disyuntor:
Implemente la interfaz CircuitBreakerConfigRegistrar
y 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
se especifica mediante circuitBreakerConfigName
. Incluya retrofit.degrade.global-resilience4j-degrade.circuit-breaker-config-name
o @Resilience4jDegrade.circuitBreakerConfigName
Si el usuario necesita utilizar otra implementación de degradación de disyuntores, herede BaseRetrofitDegrade
y configúrelo Spring Bean
.
Si @RetrofitClient
no establece fallback
o fallbackFactory
, cuando se activa el disyuntor, se lanzará RetrofitBlockException
directamente. Los usuarios pueden personalizar el valor de retorno del método cuando se produce la fusión configurando fallback
o fallbackFactory
.
Nota: La clase
fallback
debe ser la clase de implementación de la interfaz actual,fallbackFactory
debe serFallbackFactory<T>
y el tipo de parámetro genérico debe ser el tipo de interfaz actual. Además, las instanciasfallback
yfallbackFactory
deben configurarse comoSpring Bean
.
En comparación con fallbackFactory
fallback
es que puede detectar la causa anormal (causa) de cada disyuntor. El ejemplo de referencia es el siguiente:
@ 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 ;
}
};
}
}
Cuando se produce HTTP
(incluida una excepción o los datos de respuesta no cumplen con las expectativas), el decodificador de errores puede decodificar información relacionada con HTTP
en una excepción personalizada. Puede especificar el decodificador de errores de la interfaz actual en la anotación errorDecoder errorDecoder()
de @RetrofitClient
. Un decodificador de errores personalizado debe implementar la interfaz ErrorDecoder
:
ServiceInstanceChooser
Los usuarios pueden implementar la interfaz ServiceInstanceChooser
por sí mismos, completar la lógica de selección de instancias de servicio y configurarla como Spring Bean
. Para las aplicaciones Spring Cloud
, se puede utilizar la siguiente implementación.
@ 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
y path
@ RetrofitClient ( serviceId = "user" , path = "/api/user" )
public interface ChooserOkHttpUserService {
/**
* 根据id查询用户信息
*/
@ GET ( "getUser" )
User getUser ( @ Query ( "id" ) Long id );
}
Si necesitamos realizar un procesamiento de interceptación unificado en solicitudes HTTP
de todo el sistema, podemos implementar el interceptor global GlobalInterceptor
y configurarlo 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 la interfaz NetworkInterceptor
y configúrela como spring Bean
.
Retrofit
puede adaptar el objeto Call<T>
al tipo de valor de retorno del método de interfaz a través de CallAdapterFactory
. El componente amplía algunas implementaciones CallAdapterFactory
:
BodyCallAdapterFactory
HTTP
de forma sincrónica y adapte el contenido del cuerpo de la respuesta al tipo de valor de retorno del método.BodyCallAdapterFactory
, con la prioridad más baja.ResponseCallAdapterFactory
HTTP
de forma sincrónica, adapte el contenido del cuerpo de la respuesta a Retrofit.Response<T>
y devuélvalo.ResponseCallAdapterFactory
solo se puede utilizar si el tipo de valor de retorno del método es Retrofit.Response<T>
.CallAdapterFactory
relacionado con la programación reactiva Retrofit
seleccionará el CallAdapterFactory
correspondiente para realizar el procesamiento de adaptación según el tipo de valor de retorno del método . Los tipos de valor de retorno actualmente admitidos son los siguientes:
String
: Adapte Response Body
a String
y devuélvalo.Long
/ Integer
/ Boolean
/ Float
/ Double
): adapta Response Body
a los tipos básicos anterioresJava
: Adapte Response Body
al objeto Java
correspondiente y devuélvaloCompletableFuture<T>
: adapta Response Body
a CompletableFuture<T>
y lo devuelve.Void
: Puede utilizar Void
si no le importa el tipo de devolución.Response<T>
: adapta Response
al objeto Response<T>
y la devuelve.Call<T>
: no realiza procesamiento de adaptación y devuelve directamente el objeto Call<T>
Mono<T>
: tipo de retorno reactivo Project Reactor
Single<T>
: tipo de retorno responsivo Rxjava
(compatible con Rxjava2/Rxjava3
)Completable
: tipo de retorno responsivo Rxjava
, la solicitud HTTP
no tiene cuerpo de respuesta (admite Rxjava2/Rxjava3
) CallAdapter
se puede ampliar heredando CallAdapter.Factory
.
El componente admite fábricas de adaptadores de llamadas globales a través de la configuración retrofit.global-call-adapter-factories
:
retrofit :
# 全局转换器工厂(组件扩展的`CallAdaptorFactory`工厂已经内置,这里请勿重复配置)
global-call-adapter-factories :
# ...
Para cada interfaz Java, también puede especificar CallAdapter.Factory
utilizado por la interfaz actual a través de @RetrofitClient.callAdapterFactories
.
Sugerencia: configurar
CallAdapter.Factory
comoSpring Bean
Retrofit
utiliza Converter
para convertir el objeto anotado @Body
en Request Body
y convertir Response Body
en un objeto Java
. Puede elegir entre los siguientes Converter
:
El componente admite la configuración Converter.Factory
global a través de retrofit.global-converter-factories
. El valor predeterminado es retrofit2.converter.jackson.JacksonConverterFactory
.
Si necesita modificar la configuración Jackson
, simplemente sobrescriba usted mismo la configuración del bean
de JacksonConverterFactory
.
retrofit :
# 全局转换器工厂
global-converter-factories :
- com.github.lianjiatech.retrofit.spring.boot.core.BasicTypeConverterFactory
- retrofit2.converter.jackson.JacksonConverterFactory
Para cada interfaz Java
, también puede especificar Converter.Factory
utilizado por la interfaz actual a través de @RetrofitClient.converterFactories
.
Sugerencia: Configure
Converter.Factory
comoSpring Bean
.
Anotaciones como @RetrofitClient
, @Retry
, @Logging
y @Resilience4jDegrade
admiten metaanotaciones, herencia y @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 ();
}
}
Las URL dinámicas se pueden implementar utilizando la anotación @url
. En este momento, baseUrl
se puede configurar con cualquier URL legal. Por ejemplo: http://github.com/
. En tiempo de ejecución, las solicitudes solo se iniciarán según la dirección @Url
.
Nota:
@url
debe colocarse en la primera posición de los parámetros del método. Además,@GET
,@POST
y otras anotaciones no necesitan definir la ruta del punto final.
@ GET
Map < String , Object > test3 ( @ Url String url , @ Query ( "name" ) String name );
DELETE
agrega el cuerpo de la solicitud @ HTTP ( method = "DELETE" , path = "/user/delete" , hasBody = true )
GET
okhttp3
en sí no admite agregar cuerpos de solicitud GET
. El código fuente es el siguiente:
El autor dio razones específicas, puede consultar: problema
Sin embargo, si realmente necesita hacer esto, puede usar: @HTTP(method = "get", path = "/user/get", hasBody = true)
y usar get
en minúsculas para evitar las restricciones anteriores.
Si tiene alguna pregunta, plantee un problema o únase al grupo QQ para recibir comentarios.
Número de grupo: 806714302