本手册正在编写中,目前尚未完善。
如果您想帮助改进它,我们希望您能这样做,请参阅 README

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 客户端的入口点。

提供的 URIString 指定了 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 适配到以下类型时

以下示例展示了为同一个端点配置的 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();
    });
  }
}