package com.alibaba.fastjson.support.spring; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.http.server.ServletServerHttpRequest; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; /** * A convenient base class for {@code ResponseBodyAdvice} implementations * that customize the response before JSON serialization with {@link FastJsonpHttpMessageConverter4}'s concrete * subclasses. * <p> * Compatible Spring MVC version 4.2+ * * @author Jerry.Chen * @since 1.2.20 */ @ControllerAdvice public class FastJsonpResponseBodyAdvice implements ResponseBodyAdvice<Object> { /** * Pattern for validating jsonp callback parameter values. */ private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*"); private final String[] jsonpQueryParamNames; /** * Default JSONP query param names: callback/jsonp */ public static final String[] DEFAULT_JSONP_QUERY_PARAM_NAMES = { "callback", "jsonp" }; public FastJsonpResponseBodyAdvice() { this.jsonpQueryParamNames = DEFAULT_JSONP_QUERY_PARAM_NAMES; } public FastJsonpResponseBodyAdvice(String... queryParamNames) { Assert.isTrue(!ObjectUtils.isEmpty(queryParamNames), "At least one query param name is required"); this.jsonpQueryParamNames = queryParamNames; } public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return FastJsonpHttpMessageConverter4.class.isAssignableFrom(converterType); } public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { MappingFastJsonValue container = getOrCreateContainer(body); beforeBodyWriteInternal(container, selectedContentType, returnType, request, response); return container; } /** * Wrap the body in a {@link MappingFastJsonValue} value container (for providing * additional serialization instructions) or simply cast it if already wrapped. */ protected MappingFastJsonValue getOrCreateContainer(Object body) { return (body instanceof MappingFastJsonValue ? (MappingFastJsonValue) body : new MappingFastJsonValue(body)); } /** * Invoked only if the converter type is {@code FastJsonpHttpMessageConverter4}. */ public void beforeBodyWriteInternal(MappingFastJsonValue bodyContainer, MediaType contentType, MethodParameter returnType, ServerHttpRequest request, ServerHttpResponse response) { HttpServletRequest servletRequest = ((ServletServerHttpRequest) request).getServletRequest(); for (String name : this.jsonpQueryParamNames) { String value = servletRequest.getParameter(name); if (value != null) { if (!isValidJsonpQueryParam(value)) { continue; } MediaType contentTypeToUse = getContentType(contentType, request, response); response.getHeaders().setContentType(contentTypeToUse); bodyContainer.setJsonpFunction(value); break; } } } /** * Validate the jsonp query parameter value. The default implementation * returns true if it consists of digits, letters, or "_" and ".". * Invalid parameter values are ignored. * * @param value the query param value, never {@code null} */ protected boolean isValidJsonpQueryParam(String value) { return CALLBACK_PARAM_PATTERN.matcher(value).matches(); } /** * Return the content type to set the response to. * This implementation always returns "application/javascript". * * @param contentType the content type selected through content negotiation * @param request the current request * @param response the current response * @return the content type to set the response to */ protected MediaType getContentType(MediaType contentType, ServerHttpRequest request, ServerHttpResponse response) { return new MediaType("application", "javascript"); } }