diff --git a/src/main/java/cokr/xit/foundation/data/JSON.java b/src/main/java/cokr/xit/foundation/data/JSON.java index 406b158..177ce3b 100644 --- a/src/main/java/cokr/xit/foundation/data/JSON.java +++ b/src/main/java/cokr/xit/foundation/data/JSON.java @@ -1,9 +1,15 @@ package cokr.xit.foundation.data; import java.io.InputStream; +import java.util.Set; import com.fasterxml.jackson.core.JsonParser.Feature; +import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider; import cokr.xit.foundation.AbstractComponent; @@ -12,6 +18,7 @@ import cokr.xit.foundation.AbstractComponent; */ public class JSON extends AbstractComponent { private ObjectMapper objectMapper; + private FilterProvider filterProvider; /**JSON 포맷/파싱에 사용하는 ObjectMapper를 반환한다. * @return JSON 포맷/파싱에 사용하는 ObjectMapper @@ -20,6 +27,7 @@ public class JSON extends AbstractComponent { if (objectMapper == null) { objectMapper = new ObjectMapper(); objectMapper.configure(Feature.ALLOW_COMMENTS, true); + objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } return objectMapper; } @@ -33,6 +41,15 @@ public class JSON extends AbstractComponent { return this; } + /**객체를 JSON 문자열로 변환할 때 적용할 Filter를 반환한다.
+ * 이 때 객체의 클래스는 {@code @JsonFilter("필터 아이디")} 주석이 적용되어 있어야 한다. + * @param id 필터 아이디 + * @return Filter + */ + public Filter filter(String id) { + return new Filter().init(id, this); + } + /**JSON 문자열을 파싱하여 주어진 클래스의 객체로 변환한다. * @param 클래스 유형 * @param json JSON 문자열 @@ -80,10 +97,81 @@ public class JSON extends AbstractComponent { public String stringify(Object obj, boolean indent) { if (obj instanceof String) return (String)obj; + + ObjectMapper mapper = getObjectMapper(); + ObjectWriter writer = null; + if (!indent) { + writer = filterProvider == null ? mapper.writer() : mapper.writer(filterProvider); + } else { + writer = mapper.writerWithDefaultPrettyPrinter(); + if (filterProvider != null) + writer = writer.with(filterProvider); + } + try { - return getObjectMapper().writeValueAsString(obj); + return writer.writeValueAsString(obj); } catch (Exception e) { throw runtimeException(e); } } + + /**객체를 JSON 문자열로 변환할 때 적용할 필터 설정을 지원하는 클래스.
+ * 이 때 객체의 클래스는 {@code @JsonFilter("필터 아이디")} 주석이 적용되어 있어야 한다. + * @author mjkhan + */ + public static class Filter { + private String id; + private JSON json; + + private Filter init(String id, JSON json) { + this.id = id; + this.json = json; + return this; + } + + /**JSON 문자열로 변환할 때 포함시킬 객체의 프로퍼티 이름을 설정한다.
+ * 빈 값을 설정하면 모든 프로퍼티를 포함시킨다. + * @param properties JSON 문자열에 포함시킬 객체의 프로퍼티 이름 + * @return JSON + */ + public JSON include(Set properties) { + return setProvider(!isEmpty(properties) ? + SimpleBeanPropertyFilter.filterOutAllExcept(properties) : + SimpleBeanPropertyFilter.serializeAll() + ); + } + + /**JSON 문자열로 변환할 때 포함시킬 객체의 프로퍼티 이름을 설정한다.
+ * 빈 값을 설정하면 모든 프로퍼티를 포함시킨다. + * @param properties JSON 문자열에 포함시킬 객체의 프로퍼티 이름 + * @return JSON + */ + public JSON include(String... properties) { + return include(Set.of(properties)); + } + + /**JSON 문자열로 변환할 때 제외할 객체의 프로퍼티 이름을 설정한다. + * @param properties JSON 문자열에서 제외할 객체의 프로퍼티 이름 + * @return JSON + */ + public JSON exclude(Set properties) { + notEmpty(properties, "properties"); + return setProvider(SimpleBeanPropertyFilter.serializeAllExcept(properties)); + } + + /**JSON 문자열로 변환할 때 제외할 객체의 프로퍼티 이름을 설정한다. + * @param properties JSON 문자열에서 제외할 객체의 프로퍼티 이름 + * @return JSON + */ + public JSON exclude(String... properties) { + return exclude(Set.of(properties)); + } + + private JSON setProvider(SimpleBeanPropertyFilter filter) { + SimpleFilterProvider provider = new SimpleFilterProvider(); + provider.addFilter(id, filter); + json.filterProvider = provider; + return json; + } + } } \ No newline at end of file diff --git a/src/main/java/cokr/xit/foundation/web/WebClient.java b/src/main/java/cokr/xit/foundation/web/WebClient.java index dbf0716..d921cca 100644 --- a/src/main/java/cokr/xit/foundation/web/WebClient.java +++ b/src/main/java/cokr/xit/foundation/web/WebClient.java @@ -26,6 +26,8 @@ import java.util.Random; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; +import javax.net.ssl.SSLContext; + import cokr.xit.foundation.Assert; import cokr.xit.foundation.data.JSON; @@ -83,6 +85,7 @@ public class WebClient { private HttpClient.Version version = HttpClient.Version.HTTP_2; private Duration timeout = Duration.ofSeconds(30); private Authenticator authenticator; + private SSLContext sslContext; private ProxySelector proxy; private HttpClient.Redirect followRedirects = HttpClient.Redirect.NORMAL; @@ -114,6 +117,15 @@ public class WebClient { return this; } + /**SSL 통신을 할 경우 SSLContext를 설정한다. + * @param sslContext SSLContext + * @return 현재 WebClient + */ + public WebClient sslContext(SSLContext sslContext) { + this.sslContext = sslContext; + return this; + } + /**서버와 접속 시 프록시를 사용할 경우 proxy를 설정한다. * @param proxy 서버 접속에 사용할 프록시 설정 * @return 현재 WebClient @@ -140,6 +152,8 @@ public class WebClient { if (authenticator != null) builder.authenticator(authenticator); + if (sslContext != null) + builder.sslContext(sslContext); if (proxy != null) builder.proxy(proxy); @@ -241,6 +255,9 @@ public class WebClient { * @author mjkhan */ public static class Request { + /** 요청 성공 */ + public static final int SUCCESS = 200; + public static enum ContentType { FORM("application/x-www-form-urlencoded"), JSON("application/json"), @@ -272,6 +289,8 @@ public class WebClient { private Charset charset = StandardCharsets.UTF_8; private LinkedHashMap headers; private LinkedHashMap keyValues; + + private JSON json; private Consumer> textHandler = hresp -> { HttpHeaders headers = hresp.headers(); headers.map().forEach((k, v) -> System.out.println(k + " = " + v)); @@ -340,17 +359,18 @@ public class WebClient { return this; } - /**데이터를 JSON으로 주고받는지 설정한다. 디폴트는 false. - * @param json 데이터의 JSON 여부 - *
  • 데이터를 JSON으로 주고받으면 true
  • - *
  • 그렇지 않으면 false
  • - *
+ /**데이터를 JSON으로 변환할 JSON을 설정한다. + * @param json JSON * @return 현재 Request - public Request json(boolean json) { + */ + public Request json(JSON json) { this.json = json; - return this; + return contentType(ContentType.JSON); + } + + private JSON json() { + return Assert.ifEmpty(json, () -> json = new JSON()); } - */ /**요청의 응답이 파일인지 설정한다. 디폴트는 false(텍스트). * @param download @@ -365,7 +385,8 @@ public class WebClient { } private Object bodyData() { - if (Assert.isEmpty(keyValues)) return null; + int size = keyValues != null ? keyValues.size() : 0; + if (size != 1) return null; Object value = keyValues.remove("body"); if (value != null) @@ -448,10 +469,10 @@ public class WebClient { private String inJSON() { Object body = bodyData(); if (!Assert.isEmpty(body)) - return new JSON().stringify(body); + return json().stringify(body); if (!Assert.isEmpty(keyValues)) - return new JSON().stringify(keyValues); + return json().stringify(keyValues); return ""; }