带提供程序的基本 Webclient
要开始,我们需要一个生产 WebClient 的工厂。
public class ClientFactory {
private Map<String, WebClient> cache = new HashMap<>();
public enum RESTClient {
PORTAL;
}
public WebClient fetchRestClient(RESTClient restClient) {
if (this.cache.containsKey(restClient)) {
return WebClient.fromClient(this.cache.get(rc));
}
if (RESTClient.enum.equals(rc)) {
List<Object> providers = new ArrayList<Object>();
providers.add(new GsonMessageBodyProvider());
WebClient webClient = WebClient.create("https://blah.com", providers);
HTTPConduit conduit = WebClient.getConfig(webClient).getHttpConduit();
conduit.getClient().setReceiveTimeout(recieveTimeout);
conduit.getClient().setConnectionTimeout(connectionTimout);
this.cache.put(RESTClient.CAT_DEVELOPER_PORTAL.name(), webClient);
return WebClient.fromClient(webClient);// thread safe
}
}
}
- 首先,我们创建一个提供者列表(稍后会介绍)
- 接下来,我们使用静态工厂“
create()
”创建一个新的 webclient。在这里,我们可以添加一些其他的东西,如 Basic Auth creds 和线程安全。现在只需使用这个。 - 接下来,我们拉出 HTTPConduit 并设置超时。CXF 附带 Java 基类客户端,但你可以使用 Glassfish 或其他客户端。
- 最后我们缓存 WebClient。这很重要,因为到目前为止,代码的创建成本很高。下一行显示了如何使其线程安全。请注意,这也是我们从缓存中提取代码的方式。基本上我们正在制作 REST 调用的模型,然后在每次需要时克隆它。
- 请注意这里没有的内容:我们没有添加任何参数或 URL。这些可以在这里添加,但这将产生一个特定的端点,我们想要一个通用的端点。此外,请求中没有添加标头。这些不会超过克隆,因此必须在以后添加。
现在我们有了一个准备好的 WebClient。让我们设置其余的通话。
public Person fetchPerson(Long id) {
long timer t = System.currentTimeMillis();
Person person = null;
try {
wc = this.factory.findWebClient(RESTClient.PORTAL);
wc.header(AUTH_HEADER, SUBSCRIPTION_KEY);
wc.header(HttpHeaders.ACCEPT, "application/person-v1+json");
wc.path("person").path("base");
wc.query("id", String.valueOf(id));
person = wc.get(Person.class);
}
catch (WebApplicationException wae) {
// we wanna skip these. They will show up in the "finally" logs.
}
catch (Exception e) {
log.error(MessageFormat.format("Error fetching Person: id:{0} ", id), e);
}
finally {
log.info("GET HTTP:{} - Time:[{}ms] - URL:{} - Content-Type:{}", wc.getResponse().getStatus(), (System.currentTimeMillis() - timer), wc.getCurrentURI(), wc.getResponse().getMetadata().get("content-type"));
wc.close();
}
return p;
}
- 在尝试中,我们从工厂中获取 WebClient。这是一个新的冷淡的。
- 接下来我们设置一些标题。在这里,我们添加一些 Auth 标头,然后添加一个接受标头。请注意我们有一个自定义接受标头。
- 接下来是添加路径和查询字符串。请记住,这些步骤没有顺序。
- 最后我们做得到。当然,有几种方法可以做到这一点。在这里,我们让 CXF 为我们做 JSON 映射。完成这种方式后,我们必须处理 WebApplicationExceptions。因此,如果我们得到 404,CXF 将抛出异常。请注意,我在那里吃了那些例外,因为我只是在最后记录响应。但是,我确实希望得到任何其他例外,因为它们可能很重要。
- 如果你不喜欢此异常处理切换,则可以从
get
返回 Response 对象。该对象包含实体和 HTTP 状态代码。它永远不会抛出 WebApplicationException。唯一的缺点是它没有为你执行 JSON 映射。 - 最后,在
finally
子句中,我们有一个“wc.close()
”。如果从 get 子句中获取对象,则不必执行此操作。虽然有些东西可能会出错,但这是一个很好的故障保险。
那么,那个接受标题呢:application / person-v1 + json CXF 如何知道如何解析它?CXF 附带了一些内置的 JSON 解析器,但我想使用 Gson。Json 解析器的许多其他实现需要某种注释但不需要 Gson。它很容易使用。
public class GsonMessageBodyProvider<T> implements MessageBodyReader<T>, MessageBodyWriter<T> {
private Gson gson = new GsonBuilder().create();
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return StringUtils.endsWithIgnoreCase(mediaType.getSubtype(), "json");
}
@Override
public T readFrom(Class<T> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException {
try {
return gson.fromJson(new BufferedReader(new InputStreamReader(entityStream, "UTF-8")), type);
}
catch (Exception e) {
throw new IOException("Trouble reading into:" + type.getName(), e);
}
}
@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return StringUtils.containsIgnoreCase(mediaType.getSubtype(), "json");
}
@Override
public long getSize(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
return 0;
}
@Override
public void writeTo(T t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException, WebApplicationException {
try {
JsonWriter writer = new JsonWriter(new OutputStreamWriter(entityStream, "UTF-8"));
writer.setIndent(" ");
gson.toJson(t, type, writer);
writer.close();
}
catch (Exception e) {
throw new IOException("Trouble marshalling:" + type.getName(), e);
}
}
}
代码很简单。你实现了两个接口:MessageBodyReader 和 MessageBodyWriter(或只是一个),然后在创建 WebClient 时将其添加到提供者。CXF 从中得出结论。一种选择是在“isReadable()
”“isWriteable()
”方法中返回 true
。这将确保 CXF 使用此类而不是所有内置类。将首先检查添加的提供程序。