diff --git a/src/main/java/cokr/xit/foundation/web/WebClient.java b/src/main/java/cokr/xit/foundation/web/WebClient.java index 6f7628b..f219d9f 100644 --- a/src/main/java/cokr/xit/foundation/web/WebClient.java +++ b/src/main/java/cokr/xit/foundation/web/WebClient.java @@ -21,7 +21,54 @@ import java.util.function.Consumer; import cokr.xit.foundation.Assert; import cokr.xit.foundation.data.JSON; -/** +/**서버와의 http 통신을 지원하는 유틸리티. + *

WebClient는 {@link #get(Consumer) GET}, 또는 {@link #post(Consumer) POST} 방식의 통신을 지원한다. {@link Request 요청의 설정}에 따라 + *

+ * 으로 통신할 수 있다.
+ * 다음은 사용례이다. + * * @author mjkhan */ public class WebClient { @@ -37,26 +84,46 @@ public class WebClient { private HttpClient.Redirect followRedirects = HttpClient.Redirect.NORMAL; + /**http 프로토콜의 버젼을 설정한다. 디폴트는 HTTP/2 + * @param version http 프로토콜 버젼 + * @return 현재 WebClient + */ public WebClient version(HttpClient.Version version) { this.version = version; return this; } + /**응답 대기 시간(초)을 설정한다. 디폴트는 30초 + * @param seconds 응답 대기 시간(초) + * @return 현재 WebClient + */ public WebClient timeout(int seconds) { this.timeout = Duration.ofSeconds(seconds); return this; } + /**서버에 대한 인증을 처리할 경우 Authenticator를 설정한다. + * @param authenticator 인증 처리를 위한 Authenticator + * @return 현재 WebClient + */ public WebClient authenticator(Authenticator authenticator) { this.authenticator = authenticator; return this; } + /**서버와 접속 시 프록시를 사용할 경우 proxy를 설정한다. + * @param proxy 서버 접속에 사용할 프록시 설정 + * @return 현재 WebClient + */ public WebClient proxy(ProxySelector proxy) { this.proxy = proxy; return this; } + /**서버의 응답이 redirect일 경우 대응 방법을 설정한다. 디폴트는 HttpClient.Redirect.NORMAL. + * @param redirect redirect 응답에 대한 대응 방법 + * @return 현재 WebClient + */ public WebClient followRedirects(HttpClient.Redirect redirect) { this.followRedirects = redirect; return this; @@ -76,20 +143,25 @@ public class WebClient { return builder.build(); } + /**"GET" 방식의 요청을 전송한다. + * @param 요청의 body 핸들러 유형 + * @param configurer "GET" 방식 요청의 설정자 + * @return http 응답 + */ @SuppressWarnings("unchecked") - public HttpResponse get(Consumer configurer) { - Config conf = new Config(); - configurer.accept(conf); - HttpRequest hreq = conf.get(); + public HttpResponse get(Consumer configurer) { + Request req = new Request(); + configurer.accept(req); + HttpRequest hreq = req.get(); try { - if (conf.download) - return (HttpResponse)handleRequest(conf.async, hreq, HttpResponse.BodyHandlers.ofInputStream(), conf.downloadHandler); + if (req.download) + return (HttpResponse)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofInputStream(), req.downloadHandler); else - return (HttpResponse)handleRequest(conf.async, hreq, HttpResponse.BodyHandlers.ofString(), conf.textHandler); + return (HttpResponse)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofString(), req.textHandler); } catch (Throwable e) { - if (conf.async) { - conf.errorHandler.accept(Assert.rootCause(e)); + if (req.async) { + req.errorHandler.accept(Assert.rootCause(e)); return null; } else throw Assert.runtimeException(e); @@ -109,26 +181,35 @@ public class WebClient { } } + /**"POST" 방식의 요청을 전송한다. + * @param 요청의 body 핸들러 유형 + * @param configurer "POST" 방식 요청의 설정자 + * @return http 응답 + */ @SuppressWarnings("unchecked") - public HttpResponse post(Consumer configurer) { - Config conf = new Config(); - configurer.accept(conf); - HttpRequest hreq = conf.post(); + public HttpResponse post(Consumer configurer) { + Request req = new Request(); + configurer.accept(req); + HttpRequest hreq = req.post(); try { - if (conf.download) - return (HttpResponse)handleRequest(conf.async, hreq, HttpResponse.BodyHandlers.ofInputStream(), conf.downloadHandler); + if (req.download) + return (HttpResponse)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofInputStream(), req.downloadHandler); else - return (HttpResponse)handleRequest(conf.async, hreq, HttpResponse.BodyHandlers.ofString(), conf.textHandler); + return (HttpResponse)handleRequest(req.async, hreq, HttpResponse.BodyHandlers.ofString(), req.textHandler); } catch (Throwable e) { - if (conf.async) { - conf.errorHandler.accept(Assert.rootCause(e)); + if (req.async) { + req.errorHandler.accept(Assert.rootCause(e)); return null; } else throw Assert.runtimeException(e); } } + /**응답 유형이 다운로드한 파일일 경우, 해당 파일을 지정한 경로로 저장한다. + * @param inputStream 다운로드한 파일 + * @param path 저장할 경로 + */ public void write(InputStream inputStream, String path) { try ( InputStream in = inputStream; @@ -140,7 +221,23 @@ public class WebClient { } } - public static class Config { + /**전송할 http 요청의 설정. + *

Request로 다음 항목들을 설정한다. + *

  • {@link #uri(String) uri}: 대상 서버의 uri
  • + *
  • {@link #json(boolean) json}: 서버와 주고받는 데이터의 JSON 여부
  • + *
  • {@link #async(boolean) async}: 서버와의 통신이 비동기인지 여부
  • + *
  • {@link #download(boolean) download}: 응답이 파일 다운로드인지 여부
  • + *
  • {@link #header(String, String) header}: http 헤더
  • + *
  • {@link #data(String, Object) data}: 서버에 전송하는 데이터
  • + *
+ * 서버와 비동기로 통신할 경우 다음 항목들을 설정한다. + *
  • {@link #onResponse(Consumer) onResponse}: 텍스트 응답 핸들러
  • + *
  • {@link #onDownload(Consumer) onDownload}: 다운로드한 파일 핸들러
  • + *
  • {@link #onError(Throwable) onError}: 오류 핸들러
  • + *
+ * @author mjkhan + */ + public static class Request { private String uri; private boolean async, @@ -160,56 +257,107 @@ public class WebClient { t.printStackTrace(System.err); }; - public Config header(String key, String value) { + /**요청의 헤더를 설정한다. + * @param key 헤더이름 + * @param value 헤더값 + * @return 현재 Request + */ + public Request header(String key, String value) { if (headers == null) headers = new LinkedHashMap<>(); headers.put(key, value); return this; } - public Config uri(String uri) { + /**요청의 uri를 설정한다. + * @param uri 요청 대상 서버의 uri + * @return 현재 Request + */ + public Request uri(String uri) { this.uri = uri; return this; } - public Config charset(Charset charset) { + /**요청의 문자셋을 설정한다. 디폴트는 UTF-8. + * @param charset 요청의 문자셋 + * @return 현재 Request + */ + public Request charset(Charset charset) { this.charset = charset; return this; } - public Config async(boolean async) { + /**요청이 비동기인지 설정한다. 디폴트는 false(동기). + * @param async 요청의 비동기 여부 + *
  • 요청이 비동기면 true
  • + *
  • 요청이 동기면 false
  • + *
+ * @return 현재 Request + */ + public Request async(boolean async) { this.async = async; return this; } - public Config json(boolean json) { + /**데이터를 JSON으로 주고받는지 설정한다. 디폴트는 false. + * @param json 데이터의 JSON 여부 + *
  • 데이터를 JSON으로 주고받으면 true
  • + *
  • 그렇지 않으면 false
  • + *
+ * @return 현재 Request + */ + public Request json(boolean json) { this.json = json; return this; } - public Config download(boolean download) { + /**요청의 응답이 파일인지 설정한다. 디폴트는 false(텍스트). + * @param download + *
  • 응답으로 file을 다운받으면 true
  • + *
  • 그렇지 않으면 false
  • + *
+ * @return 현재 Request + */ + public Request download(boolean download) { this.download = download; return this; } - public Config data(String key, Object value) { + /**요청으로 전달할 데이터를 설정한다. + * @param key 데이터 키(이름) + * @param value 데이터 값 + * @return 현재 Request + */ + public Request data(String key, Object value) { if (data == null) data = new LinkedHashMap<>(); data.put(key, value); return this; } - public Config onResponse(Consumer> handler) { + /**비동기 요청의 텍스트 응답을 처리하는 핸들러를 설정한다. + * @param handler 비동기 요청의 텍스트 응답을 처리하는 핸들러 + * @return 현재 Request + */ + public Request onResponse(Consumer> handler) { textHandler = handler; return this; } - public Config onDownload(Consumer> handler) { + /**비동기 요청의 다운로드 파일을 처리하는 핸들러를 설정한다. + * @param handler 비동기 요청의 다운로드 파일을 처리하는 핸들러 + * @return 현재 Request + */ + public Request onDownload(Consumer> handler) { downloadHandler = handler; return this; } - public Config onError(Consumer handler) { + /**비동기 요청의 오류를 처리하는 핸들러를 설정한다. + * @param handler 오류 핸들러 + * @return 현재 Request + */ + public Request onError(Consumer handler) { errorHandler = handler; return this; }