mirror of
https://github.com/apache/struts.git
synced 2026-08-06 07:06:58 +00:00
Merge pull request #692 from apache/WW-5310-fragment
[WW-5310] Supports fragment in URL
This commit is contained in:
@@ -159,10 +159,10 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> actionParams = null;
|
||||
QueryStringParser.Result queryStringResult = queryStringParser.empty();
|
||||
if (action != null && action.indexOf('?') > 0) {
|
||||
String queryString = action.substring(action.indexOf('?') + 1);
|
||||
actionParams = queryStringParser.parse(queryString, false);
|
||||
queryStringResult = queryStringParser.parse(queryString);
|
||||
action = action.substring(0, action.indexOf('?'));
|
||||
}
|
||||
|
||||
@@ -176,7 +176,7 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
|
||||
ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters);
|
||||
String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping),
|
||||
formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false);
|
||||
formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true, false, false);
|
||||
formComponent.addParameter("action", result);
|
||||
|
||||
// let's try to get the actual action class and name
|
||||
@@ -213,7 +213,7 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
LOG.warn("No configuration found for the specified action: '{}' in namespace: '{}'. Form action defaulting to 'action' attribute's literal value.", actionName, namespace);
|
||||
}
|
||||
|
||||
String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true);
|
||||
String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, queryStringResult.getQueryParams(), scheme, formComponent.includeContext, true);
|
||||
formComponent.addParameter("action", result);
|
||||
|
||||
// namespace: cut out anything between the start and the last /
|
||||
@@ -291,7 +291,11 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
|
||||
private void includeGetParameters(UrlProvider urlComponent) {
|
||||
String query = extractQueryString(urlComponent);
|
||||
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false));
|
||||
QueryStringParser.Result result = queryStringParser.parse(query);
|
||||
mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), result.getQueryParams());
|
||||
if (!result.getQueryFragment().isEmpty()) {
|
||||
urlComponent.setAnchor(result.getQueryFragment());
|
||||
}
|
||||
}
|
||||
|
||||
private String extractQueryString(UrlProvider urlComponent) {
|
||||
@@ -339,7 +343,7 @@ public class ServletUrlRenderer implements UrlRenderer {
|
||||
if (StringUtils.contains(value, "?")) {
|
||||
String queryString = value.substring(value.indexOf('?') + 1);
|
||||
|
||||
mergedParams = queryStringParser.parse(queryString, false);
|
||||
mergedParams = new LinkedHashMap<>(queryStringParser.parse(queryString).getQueryParams());
|
||||
for (Map.Entry<String, ?> entry : contextParameters.entrySet()) {
|
||||
if (!mergedParams.containsKey(entry.getKey())) {
|
||||
mergedParams.put(entry.getKey(), entry.getValue());
|
||||
|
||||
@@ -33,7 +33,6 @@ import javax.servlet.RequestDispatcher;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.jsp.PageContext;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* <!-- START SNIPPET: description -->
|
||||
@@ -140,9 +139,9 @@ public class ServletDispatcherResult extends StrutsResultSupport {
|
||||
if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf('?') > 0) {
|
||||
String queryString = finalLocation.substring(finalLocation.indexOf('?') + 1);
|
||||
HttpParameters parameters = getParameters(invocation);
|
||||
Map<String, Object> queryParams = queryStringParser.parse(queryString, true);
|
||||
if (queryParams != null && !queryParams.isEmpty()) {
|
||||
parameters = HttpParameters.create(queryParams).withParent(parameters).build();
|
||||
QueryStringParser.Result queryParams = queryStringParser.parse(queryString);
|
||||
if (!queryParams.isEmpty()) {
|
||||
parameters = HttpParameters.create(queryParams.getQueryParams()).withParent(parameters).build();
|
||||
invocation.getInvocationContext().withParameters(parameters);
|
||||
// put to extraContext, see Dispatcher#createContextMap
|
||||
invocation.getInvocationContext().getContextMap().put("parameters", parameters);
|
||||
|
||||
@@ -22,11 +22,47 @@ import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Used to parse Http Query String into a Map of parameters
|
||||
* Used to parse Http Query String into a map of parameters with support for fragment
|
||||
*
|
||||
* @since Struts 6.1.0
|
||||
*/
|
||||
public interface QueryStringParser extends Serializable {
|
||||
|
||||
/**
|
||||
* @deprecated since Struts 6.2.0, use {@link #parse(String)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
Map<String, Object> parse(String queryString, boolean forceValueArray);
|
||||
|
||||
/**
|
||||
* @param queryString a query string to parse
|
||||
* @return a {@link Result} of parsing the query string
|
||||
* @since Struts 6.2.0
|
||||
*/
|
||||
Result parse(String queryString);
|
||||
|
||||
/**
|
||||
* Return an empty {@link Result}
|
||||
* @return empty result
|
||||
*/
|
||||
Result empty();
|
||||
|
||||
/**
|
||||
* Represents result of parsing query string by implementation of {@link QueryStringParser}
|
||||
*/
|
||||
interface Result {
|
||||
|
||||
Result addParam(String name, String value);
|
||||
|
||||
Result withQueryFragment(String queryFragment);
|
||||
|
||||
Map<String, Object> getQueryParams();
|
||||
|
||||
String getQueryFragment();
|
||||
|
||||
boolean contains(String name);
|
||||
|
||||
boolean isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -43,13 +43,18 @@ public class StrutsQueryStringParser implements QueryStringParser {
|
||||
|
||||
@Override
|
||||
public Map<String, Object> parse(String queryString, boolean forceValueArray) {
|
||||
return parse(queryString).getQueryParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result parse(String queryString) {
|
||||
if (StringUtils.isEmpty(queryString)) {
|
||||
LOG.debug("Query String is empty, returning an empty map");
|
||||
return Collections.emptyMap();
|
||||
return this.empty();
|
||||
}
|
||||
|
||||
Map<String, Object> queryParams = new LinkedHashMap<>();
|
||||
String[] params = queryString.split("&");
|
||||
Result queryParams = StrutsQueryStringParserResult.create();
|
||||
String[] params = extractParams(queryString);
|
||||
for (String param : params) {
|
||||
if (StringUtils.isBlank(param)) {
|
||||
LOG.debug("Param [{}] is blank, skipping", param);
|
||||
@@ -64,32 +69,98 @@ public class StrutsQueryStringParser implements QueryStringParser {
|
||||
} else {
|
||||
paramName = param;
|
||||
}
|
||||
extractParam(paramName, paramValue, queryParams, forceValueArray);
|
||||
queryParams = extractParam(paramName, paramValue, queryParams);
|
||||
}
|
||||
return queryParams;
|
||||
return queryParams.withQueryFragment(extractFragment(queryString));
|
||||
}
|
||||
|
||||
private void extractParam(String paramName, String paramValue, Map<String, Object> queryParams, boolean forceValueArray) {
|
||||
@Override
|
||||
public Result empty() {
|
||||
return new StrutsQueryStringParserResult(Collections.emptyMap(), "");
|
||||
}
|
||||
|
||||
private String[] extractParams(String queryString) {
|
||||
LOG.trace("Extracting params from query string: {}", queryString);
|
||||
String[] params = queryString.split("&");
|
||||
|
||||
int fragmentIndex = queryString.lastIndexOf("#");
|
||||
if (fragmentIndex > -1) {
|
||||
LOG.trace("Stripping fragment at index: {}", fragmentIndex);
|
||||
params = queryString.substring(0, fragmentIndex).split("&");
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
private Result extractParam(String paramName, String paramValue, Result queryParams) {
|
||||
String decodedParamName = decoder.decode(paramName, true);
|
||||
String decodedParamValue = decoder.decode(paramValue, true);
|
||||
return queryParams.addParam(decodedParamName, decodedParamValue);
|
||||
}
|
||||
|
||||
if (queryParams.containsKey(decodedParamName) || forceValueArray) {
|
||||
// WW-1619 append new param value to existing value(s)
|
||||
Object currentParam = queryParams.get(decodedParamName);
|
||||
if (currentParam instanceof String) {
|
||||
queryParams.put(decodedParamName, new String[]{(String) currentParam, decodedParamValue});
|
||||
} else {
|
||||
String[] currentParamValues = (String[]) currentParam;
|
||||
if (currentParamValues != null) {
|
||||
List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues));
|
||||
paramList.add(decodedParamValue);
|
||||
queryParams.put(decodedParamName, paramList.toArray(new String[0]));
|
||||
private String extractFragment(String queryString) {
|
||||
int fragmentIndex = queryString.lastIndexOf("#");
|
||||
if (fragmentIndex > -1) {
|
||||
return queryString.substring(fragmentIndex + 1);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
public static class StrutsQueryStringParserResult implements Result {
|
||||
|
||||
private final Map<String, Object> queryParams;
|
||||
private String queryFragment;
|
||||
|
||||
static Result create() {
|
||||
return new StrutsQueryStringParserResult(new LinkedHashMap<>(), "");
|
||||
}
|
||||
|
||||
private StrutsQueryStringParserResult(Map<String, Object> queryParams, String queryFragment) {
|
||||
this.queryParams = queryParams;
|
||||
this.queryFragment = queryFragment;
|
||||
}
|
||||
|
||||
public Result addParam(String name, String value) {
|
||||
if (queryParams.containsKey(name)) {
|
||||
// WW-1619 append new param value to existing value(s)
|
||||
Object currentParam = queryParams.get(name);
|
||||
if (currentParam instanceof String) {
|
||||
queryParams.put(name, new String[]{(String) currentParam, value});
|
||||
} else {
|
||||
queryParams.put(decodedParamName, new String[]{decodedParamValue});
|
||||
String[] currentParamValues = (String[]) currentParam;
|
||||
if (currentParamValues != null) {
|
||||
List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues));
|
||||
paramList.add(value);
|
||||
queryParams.put(name, paramList.toArray(new String[0]));
|
||||
} else {
|
||||
queryParams.put(name, new String[]{value});
|
||||
}
|
||||
}
|
||||
} else {
|
||||
queryParams.put(name, value);
|
||||
}
|
||||
} else {
|
||||
queryParams.put(decodedParamName, decodedParamValue);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public Result withQueryFragment(String queryFragment) {
|
||||
this.queryFragment = queryFragment;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, Object> getQueryParams() {
|
||||
return Collections.unmodifiableMap(queryParams);
|
||||
}
|
||||
|
||||
public String getQueryFragment() {
|
||||
return queryFragment;
|
||||
}
|
||||
|
||||
public boolean contains(String name) {
|
||||
return queryParams.containsKey(name);
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return queryParams.isEmpty();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void testParseQuery() {
|
||||
Map<String, Object> result = parser.parse("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E", false);
|
||||
Map<String, Object> result = parser.parse("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E").getQueryParams();
|
||||
|
||||
assertEquals("aaaval", result.get("aaa"));
|
||||
assertEquals("bbbval", result.get("bbb"));
|
||||
@@ -45,7 +45,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void testParseQueryIntoArray() {
|
||||
Map<String, Object> result = parser.parse("a=1&a=2&a=3", true);
|
||||
Map<String, Object> result = parser.parse("a=1&a=2&a=3").getQueryParams();
|
||||
|
||||
Object actual = result.get("a");
|
||||
assertThat(actual).isInstanceOf(String[].class);
|
||||
@@ -54,7 +54,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void testParseEmptyQuery() {
|
||||
Map<String, Object> result = parser.parse("", false);
|
||||
Map<String, Object> result = parser.parse("").getQueryParams();
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(0, result.size());
|
||||
@@ -62,7 +62,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void testParseNullQuery() {
|
||||
Map<String, Object> result = parser.parse(null, false);
|
||||
Map<String, Object> result = parser.parse(null).getQueryParams();
|
||||
|
||||
assertNotNull(result);
|
||||
assertEquals(0, result.size());
|
||||
@@ -70,7 +70,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void testDecodeSpacesInQueryString() {
|
||||
Map<String, Object> queryParameters = parser.parse("name=value+with+space", false);
|
||||
Map<String, Object> queryParameters = parser.parse("name=value+with+space").getQueryParams();
|
||||
|
||||
assertTrue(queryParameters.containsKey("name"));
|
||||
assertEquals("value with space", queryParameters.get("name"));
|
||||
@@ -78,7 +78,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void shouldProperlySplitParamsWithDoubleEqualSign() {
|
||||
Map<String, Object> queryParameters = parser.parse("id1=n123=&id2=n3456", false);
|
||||
Map<String, Object> queryParameters = parser.parse("id1=n123=&id2=n3456").getQueryParams();
|
||||
|
||||
assertTrue(queryParameters.containsKey("id1"));
|
||||
assertTrue(queryParameters.containsKey("id2"));
|
||||
@@ -88,7 +88,7 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void shouldHandleParamWithNoValue1() {
|
||||
Map<String, Object> queryParameters = parser.parse("paramNoValue", false);
|
||||
Map<String, Object> queryParameters = parser.parse("paramNoValue").getQueryParams();
|
||||
|
||||
assertTrue(queryParameters.containsKey("paramNoValue"));
|
||||
assertEquals("", queryParameters.get("paramNoValue"));
|
||||
@@ -96,13 +96,22 @@ public class StrutsQueryStringParserTest {
|
||||
|
||||
@Test
|
||||
public void shouldHandleParamWithNoValue2() {
|
||||
Map<String, Object> queryParameters = parser.parse("paramNoValue¶m1=1234", false);
|
||||
Map<String, Object> queryParameters = parser.parse("paramNoValue¶m1=1234").getQueryParams();
|
||||
|
||||
assertTrue(queryParameters.containsKey("paramNoValue"));
|
||||
assertTrue(queryParameters.containsKey("param1"));
|
||||
assertEquals("1234", queryParameters.get("param1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHandleParamAndFragment() {
|
||||
QueryStringParser.Result queryParameters = parser.parse("param1=1234#test");
|
||||
|
||||
assertTrue(queryParameters.getQueryParams().containsKey("param1"));
|
||||
assertEquals("1234", queryParameters.getQueryParams().get("param1"));
|
||||
assertEquals("test", queryParameters.getQueryFragment());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
this.parser = new StrutsQueryStringParser(new StrutsUrlDecoder());
|
||||
|
||||
Reference in New Issue
Block a user