[Spring] spring-boot 2.1(SpringFramework 5.1)에서 없어진 기능, JSONP 간단하게 구현하기
안녕하세요, 하마연구소입니다.
SpringFramework에서 JSONP 처리를 기본 기능으로 제공하였지만, 스프링부트 2.1(스프링프레임워크 5.1)부터 없어졌습니다.
이미 Deprecated 처리되어 언제가는 없어질 것을 예상했지만, 담당하고 있는 시스템에서는 아직 JSONP가 필요한 상황입니다.
없어진 AbstractJsonpResponseBodyAdvice.java 소스
https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractJsonpResponseBodyAdvice.java
Spring에서는 기본 기능으로 빠졌어도 3rd 파트 라이브러리가 있겠지 싶어 폭풍검색을 했지만 찾을 수 없었습니다.
그래서 그냥 직접 구현하려고 하였지만, 은근히 스프링 코어쪽 소스를 이곳저곳 건드려야하는 복잡함을 느꼈습니다.
그래서 최소한의 소스로 최대한 직관적으로 JSONP를 처리할 수 있도록 구현했습니다.
SpringFramework 5.1 이전 버전에서는 AbstractJackson2HttpMessageConverter나 JsonView 등 이곳저곳에서 JSONP 처리를 위한 코딩이 되어있습니다.
위 코드는 다소 억지스럽게 강제로 JSONP 처리가 되도록 한 것이며, 다른 환경에서는 수행되지 않을 수도 있는 점 참고부탁드립니다.
감사합니다.
SpringFramework에서 JSONP 처리를 기본 기능으로 제공하였지만, 스프링부트 2.1(스프링프레임워크 5.1)부터 없어졌습니다.
이미 Deprecated 처리되어 언제가는 없어질 것을 예상했지만, 담당하고 있는 시스템에서는 아직 JSONP가 필요한 상황입니다.
없어진 AbstractJsonpResponseBodyAdvice.java 소스
https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractJsonpResponseBodyAdvice.java
Spring에서는 기본 기능으로 빠졌어도 3rd 파트 라이브러리가 있겠지 싶어 폭풍검색을 했지만 찾을 수 없었습니다.
그래서 그냥 직접 구현하려고 하였지만, 은근히 스프링 코어쪽 소스를 이곳저곳 건드려야하는 복잡함을 느꼈습니다.
그래서 최소한의 소스로 최대한 직관적으로 JSONP를 처리할 수 있도록 구현했습니다.
JsonpAdvice.java
- ControllerAdvice로 request 파라미터에 jsonp 또는 callback이 있으면 JSONP로 처리하기
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 | import org.apache.commons.lang3.StringUtils; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.json.MappingJacksonValue; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.AbstractMappingJacksonResponseBodyAdvice; import javax.servlet.http.HttpServletRequest; import java.util.regex.Pattern; /** * @see "AbstractJsonpResponseBodyAdvice" in SpringFramework 5.X (https://github.com/spring-projects/spring-framework/blob/5.0.x/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractJsonpResponseBodyAdvice.java) */ @ControllerAdvice public class JsonpAdvice extends AbstractMappingJacksonResponseBodyAdvice { /** * Pattern for validating jsonp callback parameter values. */ public static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*"); public static final MediaType JSONP_MEDIA_TYPE = new MediaType("application", "javascript"); public static final String[] JSONP_QUERY_PARAMETER_NAMES = {"jsonp", "callback"}; @Override protected void beforeBodyWriteInternal(MappingJacksonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) { HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); for (String jsonpQueryParameterName : JSONP_QUERY_PARAMETER_NAMES) { String jsonpFunctionName = servletRequest.getParameter(jsonpQueryParameterName); if (StringUtils.isNotBlank(jsonpFunctionName) == true) { if (CALLBACK_PARAM_PATTERN.matcher(jsonpFunctionName).matches() == true) { // JSONP용 MediaType 설정 response.getHeaders().setContentType(JSONP_MEDIA_TYPE); // Jackson에 의해서 JSONP로 처리할 수 있는 JsonpWrappingObject 인스턴스 생성 JsonpWrappingObject jsonpWrappingObject = JsonpWrappingObject.builder() .jsonpFunctionName(jsonpFunctionName) .value(bodyContainer.getValue()) .build(); // MappingJacksonValue에 JsonpWrappingObject 값 설정 bodyContainer.setValue(jsonpWrappingObject); break; } } } } } |
JsonpWrappingObject.java
- 기존 MappingJacksonValue.java에 jsonpFunction 필드 변수가 없어졌기 때문에 이제 더이상 HttpMessageConverter에게 응답을 JSONP로 처리하도록 알려줄 수 없음
- 따라서 응답 객체를 wrapping하여 JSONP로 처리할 수 있도록 함
1 2 3 4 5 6 7 8 9 10 11 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; import lombok.Data; @Data @Builder @JsonSerialize(using = JsonpWrappingObjectJsonSerializer.class) public class JsonpWrappingObject { private String jsonpFunctionName; private Object value; } |
JsonpWrappingObjectJsonSerializer.java
- JSONP 형식으로 응답하기 위한 커스텀 JsonSerializer를 구현
1 2 3 4 5 6 7 8 9 10 11 | import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.Builder; import lombok.Data; @Data @Builder @JsonSerialize(using = JsonpWrappingObjectJsonSerializer.class) public class JsonpWrappingObject { private String jsonpFunctionName; private Object value; } |
SpringFramework 5.1 이전 버전에서는 AbstractJackson2HttpMessageConverter나 JsonView 등 이곳저곳에서 JSONP 처리를 위한 코딩이 되어있습니다.
위 코드는 다소 억지스럽게 강제로 JSONP 처리가 되도록 한 것이며, 다른 환경에서는 수행되지 않을 수도 있는 점 참고부탁드립니다.
감사합니다.
댓글
댓글 쓰기