帶提供程式的基本 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 使用此類而不是所有內建類。將首先檢查新增的提供程式。