21 Retrofit 类型安全的客户端
ratpack-retrofit2
扩展为 retrofit2 库(v2.9.0)提供了声明式类型安全的 HTTP 客户端集成。
retrofit 库允许通过类型安全的接口来表示 HTTP API。这允许应用程序代码保持对底层 API 设计和实现的无感知,并专注于与 API 交互的行为方面。
使用 RatpackRetrofit
类生成的 retrofit 客户端由 Ratpack 的 HttpClient
支持,并且能够与 Ratpack 的 Promise
结构作为返回值类型进行交互。
通过使用 ratpack-retrofit2
集成,开发人员可以从将 API 结构隔离为类和方法注释中获益,同时仍然利用基于 Netty 的 HttpClient
的非阻塞性质和 Ratpack 的执行模型。
1.21 用法
RatpackRetrofit.client(URI endpoint)
和 RatpackRetrofit.client(String endpoint)
方法提供了创建 API 客户端的入口点。
提供的 URI
或 String
指定了 Builder
生成的所有客户端的基 URL。
可以使用 RatpackRetrofit.Builder.configure(Action<? super Retrofit.Builder> spec)
方法配置底层的 Retrofit.Builder
。
配置完成后,通过使用 api 接口调用 build(Class<T> api)
来构建客户端。此方法返回接口的生成实例,该实例将发出 HTTP 请求并将响应适配到配置的返回值类型。
import ratpack.exec.Promise;
import ratpack.retrofit.RatpackRetrofit;
import ratpack.test.embed.EmbeddedApp;
import retrofit2.http.GET;
import static org.junit.jupiter.api.Assertions.*;
public class Example {
public static interface HelloApi {
@GET("hi") Promise<String> hello();
}
public static void main(String... args) throws Exception {
EmbeddedApp api = EmbeddedApp.of(s -> s
.handlers(chain -> chain
.get("hi", ctx -> ctx.render("hello"))
)
);
EmbeddedApp.of(s -> s
.registryOf(r ->
r.add(HelloApi.class,
RatpackRetrofit
.client(api.getAddress())
.build(HelloApi.class))
)
.handlers(chain -> {
chain.get(ctx -> {
HelloApi helloApi = ctx.get(HelloApi.class);
ctx.render(helloApi.hello());
});
})
).test(httpClient -> {
assertEquals("hello", httpClient.getText());
api.close();
});
}
}
2.21 在 Retrofit API 中使用 Ratpack Promises
ratpack-retrofit2
集成支持将 Ratpack 的 Promise
用作客户端接口的返回值类型。它支持将 Promise
适配到以下类型时
- 简单标量(
Integer
、String
、Long
等) - Retrofit
Response
- Ratpack
ReceivedResponse
以下示例展示了为同一个端点配置的 3 种变体。
import ratpack.exec.Promise;
import ratpack.core.http.client.ReceivedResponse;
import retrofit2.Response;
import retrofit2.http.GET;
public interface Example {
@GET("hi") Promise<String> hello();
@GET("hi") Promise<Response<String>> helloResponse();
@GET("hi") Promise<ReceivedResponse> helloRaw();
}
3.21 创建多个 API 实现
许多 API 可以使用不同的接口来表示不同的功能。要创建多个客户端,可以获取底层的 Retrofit
类。
import ratpack.exec.Promise;
import ratpack.retrofit.RatpackRetrofit;
import ratpack.test.embed.EmbeddedApp;
import retrofit2.http.GET;
import retrofit2.Retrofit;
import static org.junit.jupiter.api.Assertions.*;
public class Example {
public static interface HelloApi {
@GET("hi") Promise<String> hello();
}
public static interface GoodbyeApi {
@GET("bye") Promise<String> bye();
}
public static void main(String... args) throws Exception {
EmbeddedApp api = EmbeddedApp.of(s -> s
.handlers(chain -> chain
.get("hi", ctx -> ctx.render("hello"))
.get("bye", ctx -> ctx.render("goodbye"))
)
);
EmbeddedApp.of(s -> s
.registryOf(r -> {
Retrofit retrofit = RatpackRetrofit
.client(api.getAddress())
.retrofit();
r.add(HelloApi.class, retrofit.create(HelloApi.class));
r.add(GoodbyeApi.class, retrofit.create(GoodbyeApi.class));
})
.handlers(chain -> {
chain.get(ctx -> {
HelloApi hiApi = ctx.get(HelloApi.class);
GoodbyeApi byeApi = ctx.get(GoodbyeApi.class);
ctx.render(hiApi.hello().right(byeApi.bye()).map(p -> p.left() + " and " + p.right()));
});
})
).test(httpClient -> {
assertEquals("hello and goodbye", httpClient.getText());
api.close();
});
}
}
4.21 使用 Retrofit 转换器
默认情况下,ratpack-retrofit2
注册 ScalarsConverterFactory
。这允许将 API 响应转换为 Java String
、基本类型及其包装类型。
如果远程 API 以 JSON 格式响应,则必须注册 JacksonConverterFactory
。
import com.fasterxml.jackson.databind.ObjectMapper;
import ratpack.exec.Promise;
import ratpack.retrofit.RatpackRetrofit;
import ratpack.exec.registry.Registry;
import ratpack.test.embed.EmbeddedApp;
import retrofit2.converter.jackson.JacksonConverterFactory;
import retrofit2.http.GET;
import retrofit2.Retrofit;
import java.util.List;
import static ratpack.core.jackson.Jackson.json;
import static org.junit.jupiter.api.Assertions.*;
public class Example {
public static interface NameApi {
@GET("names") Promise<List<String>> names();
}
public static void main(String... args) throws Exception {
EmbeddedApp api = EmbeddedApp.of(s -> s
.handlers(chain -> chain
.get("names", ctx -> ctx.render(json(new String[]{"John", "Jane"})))
)
);
EmbeddedApp.of(s -> s
.registry(r ->
Registry.single(NameApi.class,
RatpackRetrofit
.client(api.getAddress())
.configure(b ->
b.addConverterFactory(
JacksonConverterFactory.create(
r.get(ObjectMapper.class)
)
)
)
.build(NameApi.class))
)
.handlers(chain -> {
chain.get(ctx -> {
ctx.get(NameApi.class).names().then(nameList -> ctx.render(json(nameList)));
});
})
).test(httpClient -> {
assertEquals("[\"John\",\"Jane\"]", httpClient.getText());
api.close();
});
}
}