Remove HttpRequestPathHelper
The use of the undecoded URL path by default and the removal of suffix pattern matching effectively means HttpRequestPathHelper is no longer needed. Issue: SPR-15640, SPR-15639
This commit is contained in:
@@ -1,153 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.server.support;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.util.UriUtils;
|
||||
|
||||
/**
|
||||
* A helper class to obtain the lookup path for path matching purposes.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 5.0
|
||||
*/
|
||||
public class HttpRequestPathHelper {
|
||||
|
||||
private boolean urlDecode = false;
|
||||
|
||||
|
||||
// TODO: sanitize path, default/request encoding?, remove path params?
|
||||
|
||||
/**
|
||||
* Set if the request path should be URL-decoded.
|
||||
* <p>Default is "true".
|
||||
* @see UriUtils#decode(String, String)
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.urlDecode = urlDecode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the request path should be URL decoded.
|
||||
*/
|
||||
public boolean shouldUrlDecode() {
|
||||
return this.urlDecode;
|
||||
}
|
||||
|
||||
|
||||
private String decode(ServerWebExchange exchange, String path) {
|
||||
// TODO: look up request encoding?
|
||||
try {
|
||||
return UriUtils.decode(path, "UTF-8");
|
||||
}
|
||||
catch (UnsupportedEncodingException ex) {
|
||||
// Should not happen
|
||||
throw new IllegalStateException("Could not decode request string [" + path + "]");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given URI path variables unless {@link #setUrlDecode(boolean)}
|
||||
* is set to {@code true} in which case it is assumed the URL path from
|
||||
* which the variables were extracted is already decoded through a call to
|
||||
* {@link #getLookupPathForRequest(ServerWebExchange)}.
|
||||
* @param exchange current exchange
|
||||
* @param vars URI variables extracted from the URL path
|
||||
* @return the same Map or a new Map instance
|
||||
*/
|
||||
public Map<String, String> decodePathVariables(ServerWebExchange exchange, Map<String, String> vars) {
|
||||
if (this.urlDecode) {
|
||||
return vars;
|
||||
}
|
||||
Map<String, String> decodedVars = new LinkedHashMap<>(vars.size());
|
||||
for (Map.Entry<String, String> entry : vars.entrySet()) {
|
||||
decodedVars.put(entry.getKey(), decode(exchange, entry.getValue()));
|
||||
}
|
||||
return decodedVars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the given string with matrix variables. An example string would look
|
||||
* like this {@code "q1=a;q1=b;q2=a,b,c"}. The resulting map would contain
|
||||
* keys {@code "q1"} and {@code "q2"} with values {@code ["a","b"]} and
|
||||
* {@code ["a","b","c"]} respectively.
|
||||
* <p>The returned values are decoded unless {@link #setUrlDecode(boolean)}
|
||||
* is set to {@code true} in which case it is assumed the URL path from
|
||||
* which the variables were extracted is already decoded through a call to
|
||||
* {@link #getLookupPathForRequest(ServerWebExchange)}.
|
||||
* @param semicolonContent path parameter content to parse
|
||||
* @return a map with matrix variable names and values (never {@code null})
|
||||
*/
|
||||
public MultiValueMap<String, String> parseMatrixVariables(ServerWebExchange exchange,
|
||||
String semicolonContent) {
|
||||
|
||||
MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
|
||||
if (!StringUtils.hasText(semicolonContent)) {
|
||||
return result;
|
||||
}
|
||||
StringTokenizer pairs = new StringTokenizer(semicolonContent, ";");
|
||||
while (pairs.hasMoreTokens()) {
|
||||
String pair = pairs.nextToken();
|
||||
int index = pair.indexOf('=');
|
||||
if (index != -1) {
|
||||
String name = pair.substring(0, index);
|
||||
String rawValue = pair.substring(index + 1);
|
||||
for (String value : StringUtils.commaDelimitedListToStringArray(rawValue)) {
|
||||
result.add(name, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.add(pair, "");
|
||||
}
|
||||
}
|
||||
return decodeMatrixVariables(exchange, result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decode the given matrix variables unless {@link #setUrlDecode(boolean)}
|
||||
* is set to {@code true} in which case it is assumed the URL path from
|
||||
* which the variables were extracted is already decoded through a call to
|
||||
* {@link #getLookupPathForRequest(ServerWebExchange)}.
|
||||
* @param exchange current exchange
|
||||
* @param vars URI variables extracted from the URL path
|
||||
* @return the same Map or a new Map instance
|
||||
*/
|
||||
private MultiValueMap<String, String> decodeMatrixVariables(ServerWebExchange exchange,
|
||||
MultiValueMap<String, String> vars) {
|
||||
|
||||
if (this.urlDecode) {
|
||||
return vars;
|
||||
}
|
||||
MultiValueMap<String, String> decodedVars = new LinkedMultiValueMap<>(vars.size());
|
||||
for (String key : vars.keySet()) {
|
||||
for (String value : vars.get(key)) {
|
||||
decodedVars.add(key, decode(exchange, value));
|
||||
}
|
||||
}
|
||||
return decodedVars;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
/**
|
||||
* Helper classes on top of {@code org.springframework.web.server},
|
||||
* as a convenience for working with {@code ServerWebExchange}.
|
||||
*/
|
||||
@NonNullApi
|
||||
package org.springframework.web.server.support;
|
||||
|
||||
import org.springframework.lang.NonNullApi;
|
||||
@@ -1,82 +0,0 @@
|
||||
/*
|
||||
* Copyright 2002-2017 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.server.support;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link HttpRequestPathHelper}.
|
||||
* @author Rossen Stoyanchev
|
||||
*/
|
||||
public class HttpRequestPathHelperTests {
|
||||
|
||||
|
||||
@Test
|
||||
public void parseMatrixVariables() {
|
||||
|
||||
HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
|
||||
ServerWebExchange exchange = MockServerHttpRequest.get("").toExchange();
|
||||
MultiValueMap<String, String> variables;
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, null);
|
||||
assertEquals(0, variables.size());
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, "year");
|
||||
assertEquals(1, variables.size());
|
||||
assertEquals("", variables.getFirst("year"));
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, "year=2012");
|
||||
assertEquals(1, variables.size());
|
||||
assertEquals("2012", variables.getFirst("year"));
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, "year=2012;colors=red,blue,green");
|
||||
assertEquals(2, variables.size());
|
||||
assertEquals(Arrays.asList("red", "blue", "green"), variables.get("colors"));
|
||||
assertEquals("2012", variables.getFirst("year"));
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, ";year=2012;colors=red,blue,green;");
|
||||
assertEquals(2, variables.size());
|
||||
assertEquals(Arrays.asList("red", "blue", "green"), variables.get("colors"));
|
||||
assertEquals("2012", variables.getFirst("year"));
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, "colors=red;colors=blue;colors=green");
|
||||
assertEquals(1, variables.size());
|
||||
assertEquals(Arrays.asList("red", "blue", "green"), variables.get("colors"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseMatrixVariablesAndDecode() {
|
||||
|
||||
HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
|
||||
pathHelper.setUrlDecode(false);
|
||||
|
||||
ServerWebExchange exchange = MockServerHttpRequest.get("").toExchange();
|
||||
MultiValueMap<String, String> variables;
|
||||
|
||||
variables = pathHelper.parseMatrixVariables(exchange, "mvar=a%2fb");
|
||||
assertEquals(1, variables.size());
|
||||
assertEquals("a/b", variables.getFirst("mvar"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,6 @@ package org.springframework.web.reactive.config;
|
||||
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.pattern.ParsingPathMatcher;
|
||||
|
||||
/**
|
||||
@@ -35,8 +34,6 @@ public class PathMatchConfigurer {
|
||||
|
||||
private Boolean registeredSuffixPatternMatch;
|
||||
|
||||
private HttpRequestPathHelper pathHelper;
|
||||
|
||||
private PathMatcher pathMatcher;
|
||||
|
||||
|
||||
@@ -73,15 +70,6 @@ public class PathMatchConfigurer {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a {@code HttpRequestPathHelper} for the resolution of lookup paths.
|
||||
* <p>Default is {@code HttpRequestPathHelper}.
|
||||
*/
|
||||
public PathMatchConfigurer setPathHelper(HttpRequestPathHelper pathHelper) {
|
||||
this.pathHelper = pathHelper;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher for matching URL paths against registered URL patterns.
|
||||
* <p>The default is a {@link org.springframework.web.util.pattern.ParsingPathMatcher}.
|
||||
@@ -106,11 +94,6 @@ public class PathMatchConfigurer {
|
||||
return this.registeredSuffixPatternMatch;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
protected HttpRequestPathHelper getPathHelper() {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PathMatcher getPathMatcher() {
|
||||
if (this.pathMatcher instanceof ParsingPathMatcher && (this.trailingSlashMatch || this.suffixPatternMatch)) {
|
||||
|
||||
@@ -133,16 +133,10 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
if (useTrailingSlashMatch != null) {
|
||||
mapping.setUseTrailingSlashMatch(useTrailingSlashMatch);
|
||||
}
|
||||
|
||||
HttpRequestPathHelper pathHelper = configurer.getPathHelper();
|
||||
if (pathHelper != null) {
|
||||
mapping.setPathHelper(pathHelper);
|
||||
}
|
||||
PathMatcher pathMatcher = configurer.getPathMatcher();
|
||||
if (pathMatcher != null) {
|
||||
mapping.setPathMatcher(pathMatcher);
|
||||
}
|
||||
|
||||
return mapping;
|
||||
}
|
||||
|
||||
@@ -251,9 +245,6 @@ public class WebFluxConfigurationSupport implements ApplicationContextAware {
|
||||
if (pathMatchConfigurer.getPathMatcher() != null) {
|
||||
handlerMapping.setPathMatcher(pathMatchConfigurer.getPathMatcher());
|
||||
}
|
||||
if (pathMatchConfigurer.getPathHelper() != null) {
|
||||
handlerMapping.setPathHelper(pathMatchConfigurer.getPathHelper());
|
||||
}
|
||||
}
|
||||
else {
|
||||
handlerMapping = new EmptyHandlerMapping();
|
||||
|
||||
@@ -34,7 +34,6 @@ import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.pattern.ParsingPathMatcher;
|
||||
|
||||
/**
|
||||
@@ -53,8 +52,6 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
|
||||
|
||||
private int order = Integer.MAX_VALUE; // default: same as non-Ordered
|
||||
|
||||
private HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
|
||||
private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();
|
||||
@@ -76,33 +73,6 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
|
||||
return this.order;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set if the path should be URL-decoded. This sets the same property on the
|
||||
* underlying path helper.
|
||||
* @see HttpRequestPathHelper#setUrlDecode(boolean)
|
||||
*/
|
||||
public void setUrlDecode(boolean urlDecode) {
|
||||
this.pathHelper.setUrlDecode(urlDecode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link HttpRequestPathHelper} to use for resolution of lookup
|
||||
* paths. Use this to override the default implementation with a custom
|
||||
* subclass or to share common path helper settings across multiple
|
||||
* HandlerMappings.
|
||||
*/
|
||||
public void setPathHelper(HttpRequestPathHelper pathHelper) {
|
||||
this.pathHelper = pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link HttpRequestPathHelper} implementation to use for
|
||||
* resolution of lookup paths.
|
||||
*/
|
||||
public HttpRequestPathHelper getPathHelper() {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the PathMatcher implementation to use for matching URL paths
|
||||
* against registered URL patterns.
|
||||
|
||||
@@ -37,7 +37,6 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.PathMatcher;
|
||||
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.support.HttpRequestPathHelper;
|
||||
import org.springframework.web.util.pattern.ParsingPathMatcher;
|
||||
|
||||
/**
|
||||
@@ -55,8 +54,6 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
|
||||
|
||||
private PathMatcher pathMatcher = new ParsingPathMatcher();
|
||||
|
||||
private final Map<String, ResourceWebHandler> handlerMap = new LinkedHashMap<>();
|
||||
@@ -64,22 +61,6 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
|
||||
private boolean autodetect = true;
|
||||
|
||||
|
||||
/**
|
||||
* Configure a {@code HttpRequestPathHelper} to use in
|
||||
* {@link #getForRequestUrl(ServerWebExchange, String)}
|
||||
* in order to derive the lookup path for a target request URL path.
|
||||
*/
|
||||
public void setPathHelper(HttpRequestPathHelper pathHelper) {
|
||||
this.pathHelper = pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the configured {@code HttpRequestPathHelper}.
|
||||
*/
|
||||
public HttpRequestPathHelper getPathHelper() {
|
||||
return this.pathHelper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure a {@code PathMatcher} to use when comparing target lookup path
|
||||
* against resource mappings.
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package org.springframework.web.reactive.result.method;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
@@ -27,6 +28,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
@@ -34,7 +36,9 @@ import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.InvalidMediaTypeException;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.server.reactive.ServerHttpRequest;
|
||||
import org.springframework.util.LinkedMultiValueMap;
|
||||
import org.springframework.util.MultiValueMap;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.method.HandlerMethod;
|
||||
import org.springframework.web.reactive.HandlerMapping;
|
||||
import org.springframework.web.reactive.result.condition.NameValueExpression;
|
||||
@@ -123,7 +127,9 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
|
||||
// Now decode URI variables
|
||||
if (!uriVariables.isEmpty()) {
|
||||
uriVariables = getPathHelper().decodePathVariables(exchange, uriVariables);
|
||||
uriVariables = uriVariables.entrySet().stream().collect(Collectors.toMap(
|
||||
Entry::getKey, e -> StringUtils.uriDecode(e.getValue(), StandardCharsets.UTF_8)
|
||||
));
|
||||
}
|
||||
|
||||
exchange.getAttributes().put(BEST_MATCHING_PATTERN_ATTRIBUTE, bestPattern);
|
||||
@@ -156,11 +162,41 @@ public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMe
|
||||
semicolonContent = uriVarValue.substring(semicolonIndex + 1);
|
||||
uriVariables.put(uriVar.getKey(), uriVarValue.substring(0, semicolonIndex));
|
||||
}
|
||||
result.put(uriVar.getKey(), getPathHelper().parseMatrixVariables(exchange, semicolonContent));
|
||||
result.put(uriVar.getKey(), parseMatrixVariables(exchange, semicolonContent));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static MultiValueMap<String, String> parseMatrixVariables(ServerWebExchange exchange,
|
||||
String semicolonContent) {
|
||||
|
||||
MultiValueMap<String, String> vars = new LinkedMultiValueMap<>();
|
||||
if (!StringUtils.hasText(semicolonContent)) {
|
||||
return vars;
|
||||
}
|
||||
StringTokenizer pairs = new StringTokenizer(semicolonContent, ";");
|
||||
while (pairs.hasMoreTokens()) {
|
||||
String pair = pairs.nextToken();
|
||||
int index = pair.indexOf('=');
|
||||
if (index != -1) {
|
||||
String name = pair.substring(0, index);
|
||||
String rawValue = pair.substring(index + 1);
|
||||
for (String value : StringUtils.commaDelimitedListToStringArray(rawValue)) {
|
||||
vars.add(name, value);
|
||||
}
|
||||
}
|
||||
else {
|
||||
vars.add(pair, "");
|
||||
}
|
||||
}
|
||||
MultiValueMap<String, String> decoded = new LinkedMultiValueMap<>(vars.size());
|
||||
vars.forEach((key, values) -> values.forEach(value -> {
|
||||
String decodedValue = StringUtils.uriDecode(value, StandardCharsets.UTF_8);
|
||||
decoded.add(key, decodedValue);
|
||||
}));
|
||||
return decoded;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate all RequestMappingInfos once again, look if any match by URL at
|
||||
* least and raise exceptions accordingly.
|
||||
|
||||
@@ -63,7 +63,11 @@ import org.springframework.web.reactive.result.view.freemarker.FreeMarkerViewRes
|
||||
import org.springframework.web.server.ServerWebExchange;
|
||||
import org.springframework.web.server.WebHandler;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.springframework.core.ResolvableType.forClass;
|
||||
import static org.springframework.core.ResolvableType.forClassWithGenerics;
|
||||
import static org.springframework.http.MediaType.APPLICATION_FORM_URLENCODED;
|
||||
@@ -250,7 +254,6 @@ public class WebFluxConfigurationSupportTests {
|
||||
|
||||
assertEquals(Ordered.LOWEST_PRECEDENCE - 1, handlerMapping.getOrder());
|
||||
|
||||
assertNotNull(handlerMapping.getPathHelper());
|
||||
assertNotNull(handlerMapping.getPathMatcher());
|
||||
|
||||
SimpleUrlHandlerMapping urlHandlerMapping = (SimpleUrlHandlerMapping) handlerMapping;
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
|
||||
|
||||
<bean id="mapping" class="org.springframework.web.reactive.handler.SimpleUrlHandlerMapping">
|
||||
<property name="urlDecode" value="true" />
|
||||
<property name="mappings">
|
||||
<value>
|
||||
welcome.html=mainController
|
||||
|
||||
Reference in New Issue
Block a user