250x250
반응형
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로 처리하기
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로 처리할 수 있도록 한다.
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를 구현하였다.
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import java.io.IOException;
public class JsonpWrappingObjectJsonSerializer extends JsonSerializer<JsonpWrappingObject> {
@Override
public Class<JsonpWrappingObject> handledType() {
return JsonpWrappingObject.class;
}
@Override
public void serialize(JsonpWrappingObject value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
if (value != null) {
// JSONP의 prefix 부분 write
gen.writeRaw("/**/");
gen.writeRaw(value.getJsonpFunctionName());
gen.writeRaw("(");
// 값 write
gen.writeObject(value.getValue());
// JSONP의 postfix 부분 write
gen.writeRaw(");");
}
}
}
SpringFramework 5.1 이전 버전에서는 AbstractJackson2HttpMessageConverter나 JsonView 등 이곳저곳에서 JSONP 처리를 위한 코딩이 되어 있었다.
위 코드는 다소 강제로(억지로) JSONP 처리가 되도록 한 것이며, 다른/특별한 환경에서는 수행되지 않을 수도 있다.
반응형
'IT > PROGRAMMING' 카테고리의 다른 글
Java로 HTML 처리는 jsoup이 짱이네요. (0) | 2024.05.21 |
---|---|
Kotlin의 모든 클래스에서 logger 객체를 편하게 얻을 수 있는 방법 (1) | 2021.08.18 |
[Spring] spring-vault에서 @VaultPropertySource 여러개 사용하면 안되는 이유 (1) | 2019.04.19 |
YouTube(유튜브) 동영상의 썸네일 이미지 추출하는 방법 (0) | 2019.04.09 |
[Java] Gson, Jackson(ObjectMapper)으로 JSON 문자열 출력할 때, pretty printing하는 방법 (0) | 2019.04.08 |