Remove JSONP support

CORS is now widely supported and should be used instead for cross-domain
requests.

Issue: SPR-16914
This commit is contained in:
Sebastien Deleuze
2018-06-07 11:33:25 +02:00
parent 19dc981685
commit ac37b678a3
23 changed files with 25 additions and 713 deletions

View File

@@ -1,114 +0,0 @@
/*
* Copyright 2002-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.web.servlet.mvc.method.annotation;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
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.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* A convenient base class for a {@code ResponseBodyAdvice} to instruct the
* {@link org.springframework.http.converter.json.MappingJackson2HttpMessageConverter}
* to serialize with JSONP formatting.
*
* <p>Sub-classes must specify the query parameter name(s) to check for the name
* of the JSONP callback function.
*
* <p>Sub-classes are likely to be annotated with the {@code @ControllerAdvice}
* annotation and auto-detected or otherwise must be registered directly with the
* {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver}.
*
* @author Rossen Stoyanchev
* @since 4.1
*/
public abstract class AbstractJsonpResponseBodyAdvice extends AbstractMappingJacksonResponseBodyAdvice {
/**
* Pattern for validating jsonp callback parameter values.
*/
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
private final Log logger = LogFactory.getLog(getClass());
private final String[] jsonpQueryParamNames;
protected AbstractJsonpResponseBodyAdvice(String... queryParamNames) {
Assert.isTrue(!ObjectUtils.isEmpty(queryParamNames), "At least one query param name is required");
this.jsonpQueryParamNames = queryParamNames;
}
@Override
protected void beforeBodyWriteInternal(MappingJacksonValue 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)) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid jsonp parameter value: " + 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}
* @since 4.1.8
*/
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");
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2017 the original author or authors.
* Copyright 2002-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,15 +17,10 @@
package org.springframework.web.servlet.view.json;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.fasterxml.jackson.annotation.JsonView;
import com.fasterxml.jackson.core.JsonGenerator;
@@ -33,10 +28,8 @@ import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJacksonValue;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.BindingResult;
import org.springframework.web.servlet.View;
@@ -67,17 +60,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
*/
public static final String DEFAULT_CONTENT_TYPE = "application/json";
/**
* Default content type for JSONP: "application/javascript".
*/
public static final String DEFAULT_JSONP_CONTENT_TYPE = "application/javascript";
/**
* Pattern for validating jsonp callback parameter values.
*/
private static final Pattern CALLBACK_PARAM_PATTERN = Pattern.compile("[0-9A-Za-z_\\.]*");
@Nullable
private String jsonPrefix;
@@ -86,9 +68,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
private boolean extractValueFromSingleKeyModel = false;
@Nullable
private Set<String> jsonpParameterNames = new LinkedHashSet<>(Arrays.asList("jsonp", "callback"));
/**
* Construct a new {@code MappingJackson2JsonView} using default configuration
@@ -166,49 +145,6 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
this.extractValueFromSingleKeyModel = extractValueFromSingleKeyModel;
}
/**
* Set JSONP request parameter names. Each time a request has one of those
* parameters, the resulting JSON will be wrapped into a function named as
* specified by the JSONP request parameter value.
* <p>The parameter names configured by default are "jsonp" and "callback".
* @since 4.1
* @see <a href="http://en.wikipedia.org/wiki/JSONP">JSONP Wikipedia article</a>
*/
public void setJsonpParameterNames(Set<String> jsonpParameterNames) {
this.jsonpParameterNames = jsonpParameterNames;
}
@Nullable
private String getJsonpParameterValue(HttpServletRequest request) {
if (this.jsonpParameterNames != null) {
for (String name : this.jsonpParameterNames) {
String value = request.getParameter(name);
if (StringUtils.isEmpty(value)) {
continue;
}
if (!isValidJsonpQueryParam(value)) {
if (logger.isDebugEnabled()) {
logger.debug("Ignoring invalid jsonp parameter value: " + value);
}
continue;
}
return value;
}
}
return null;
}
/**
* 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}
* @since 4.1.8
*/
protected boolean isValidJsonpQueryParam(String value) {
return CALLBACK_PARAM_PATTERN.matcher(value).matches();
}
/**
* Filter out undesired attributes from the given model.
* The return value can be either another {@link Map} or a single value object.
@@ -231,58 +167,11 @@ public class MappingJackson2JsonView extends AbstractJackson2View {
return (this.extractValueFromSingleKeyModel && result.size() == 1 ? result.values().iterator().next() : result);
}
@Override
protected Object filterAndWrapModel(Map<String, Object> model, HttpServletRequest request) {
Object value = super.filterAndWrapModel(model, request);
String jsonpParameterValue = getJsonpParameterValue(request);
if (jsonpParameterValue != null) {
if (value instanceof MappingJacksonValue) {
((MappingJacksonValue) value).setJsonpFunction(jsonpParameterValue);
}
else {
MappingJacksonValue container = new MappingJacksonValue(value);
container.setJsonpFunction(jsonpParameterValue);
value = container;
}
}
return value;
}
@Override
protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
if (this.jsonPrefix != null) {
generator.writeRaw(this.jsonPrefix);
}
String jsonpFunction = null;
if (object instanceof MappingJacksonValue) {
jsonpFunction = ((MappingJacksonValue) object).getJsonpFunction();
}
if (jsonpFunction != null) {
generator.writeRaw("/**/");
generator.writeRaw(jsonpFunction + "(");
}
}
@Override
protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
String jsonpFunction = null;
if (object instanceof MappingJacksonValue) {
jsonpFunction = ((MappingJacksonValue) object).getJsonpFunction();
}
if (jsonpFunction != null) {
generator.writeRaw(");");
}
}
@Override
protected void setResponseContentType(HttpServletRequest request, HttpServletResponse response) {
if (getJsonpParameterValue(request) != null) {
response.setContentType(DEFAULT_JSONP_CONTENT_TYPE);
}
else {
super.setResponseContentType(request, response);
}
}
}