提交 3e59c244 编写于 作者: R Rossen Stoyanchev

Add UriTemplateHandler

This change introduces a strategy for expanding a URI template into a
URI and makes it a property of the RestTemplate and AsyncRestTemplate
so that they can be pre-configured with such a strategy.

The DefaultUriTemplateHandler relies on UriComponentsBuilder internally
and provides functionality equivalent to using the UriTemplate.
A DefaultUriTemplateHandler can also be configured to parse the path
of a URI template into path segments in order to allow expanding URI
variables according to path segment encoding rules.

Issue: SPR-12750
上级 2c408b70
/*
* Copyright 2002-2014 the original author or authors.
* 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.
......@@ -49,7 +49,8 @@ import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureAdapter;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.util.concurrent.SuccessCallback;
import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriTemplateHandler;
/**
* <strong>Spring's central class for asynchronous client-side HTTP access.</strong>
......@@ -146,6 +147,22 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
return this.syncTemplate.getErrorHandler();
}
/**
* Set a custom {@link UriTemplateHandler} for expanding URI templates.
* <p>By default, RestTemplate uses {@link DefaultUriTemplateHandler}.
* @param handler the URI template handler to use
*/
public void setUriTemplateHandler(UriTemplateHandler handler) {
this.syncTemplate.setUriTemplateHandler(handler);
}
/**
* Return the configured URI template handler.
*/
public UriTemplateHandler getUriTemplateHandler() {
return this.syncTemplate.getUriTemplateHandler();
}
@Override
public RestOperations getRestOperations() {
return this.syncTemplate;
......@@ -493,7 +510,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Object... urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables);
URI expanded = getUriTemplateHandler().expand(url, urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
......@@ -501,7 +518,7 @@ public class AsyncRestTemplate extends AsyncHttpAccessor implements AsyncRestOpe
public <T> ListenableFuture<T> execute(String url, HttpMethod method, AsyncRequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, ?> urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables);
URI expanded = getUriTemplateHandler().expand(url, urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
......
......@@ -23,6 +23,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.transform.Source;
import org.springframework.core.ParameterizedTypeReference;
......@@ -51,7 +52,8 @@ import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConve
import org.springframework.http.converter.xml.SourceHttpMessageConverter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.web.util.UriTemplate;
import org.springframework.web.util.DefaultUriTemplateHandler;
import org.springframework.web.util.UriTemplateHandler;
/**
* <strong>Spring's central class for synchronous client-side HTTP access.</strong>
......@@ -135,6 +137,8 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
private ResponseErrorHandler errorHandler = new DefaultResponseErrorHandler();
private UriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler();
private final ResponseExtractor<HttpHeaders> headersExtractor = new HeadersExtractor();
......@@ -226,6 +230,23 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
return this.errorHandler;
}
/**
* Set a custom {@link UriTemplateHandler} for expanding URI templates.
* <p>By default, RestTemplate uses {@link DefaultUriTemplateHandler}.
* @param handler the URI template handler to use
*/
public void setUriTemplateHandler(UriTemplateHandler handler) {
Assert.notNull(handler, "'uriTemplateHandler' is required.");
this.uriTemplateHandler = handler;
}
/**
* Return the configured URI template handler.
*/
public UriTemplateHandler getUriTemplateHandler() {
return this.uriTemplateHandler;
}
// GET
......@@ -526,7 +547,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Object... urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables);
URI expanded = getUriTemplateHandler().expand(url, urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
......@@ -534,7 +555,7 @@ public class RestTemplate extends InterceptingHttpAccessor implements RestOperat
public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback,
ResponseExtractor<T> responseExtractor, Map<String, ?> urlVariables) throws RestClientException {
URI expanded = new UriTemplate(url).expand(urlVariables);
URI expanded = getUriTemplateHandler().expand(url, urlVariables);
return doExecute(expanded, method, requestCallback, responseExtractor);
}
......
/*
* 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.util;
import java.net.URI;
import java.util.List;
import java.util.Map;
/**
* Default implementation of {@link UriTemplateHandler} that relies on
* {@link UriComponentsBuilder} internally.
*
* @author Rossen Stoyanchev
* @since 4.2
*/
public class DefaultUriTemplateHandler implements UriTemplateHandler {
private boolean parsePath;
/**
* Whether to parse the path of a URI template string into path segments.
* <p>If set to {@code true} the path of parsed URI templates is decomposed
* into path segments so that URI variables expanded into the path are
* treated according to path segment encoding rules. In effect that means the
* "/" character is percent encoded.
* <p>By default this is set to {@code false} in which case the path is kept
* as a full path and expanded URI variables will preserve "/" characters.
* @param parsePath whether to parse the path into path segments
*/
public void setParsePath(boolean parsePath) {
this.parsePath = parsePath;
}
/**
* Whether the handler is configured to parse the path into path segments.
*/
public boolean shouldParsePath() {
return this.parsePath;
}
@Override
public URI expand(String uriTemplate, Map<String, ?> uriVariables) {
UriComponentsBuilder builder = initBuilder(uriTemplate);
return builder.build().expand(uriVariables).encode().toUri();
}
@Override
public URI expand(String uriTemplate, Object... uriVariableValues) {
UriComponentsBuilder builder = initBuilder(uriTemplate);
return builder.build().expand(uriVariableValues).encode().toUri();
}
protected UriComponentsBuilder initBuilder(String uriTemplate) {
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(uriTemplate);
if (shouldParsePath()) {
List<String> pathSegments = builder.build().getPathSegments();
builder.replacePath(null);
for (String pathSegment : pathSegments) {
builder.pathSegment(pathSegment);
}
}
return builder;
}
}
/*
* 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.util;
import java.net.URI;
import java.util.Map;
/**
* A strategy for expanding a URI template with URI variables into a {@link URI}.
*
* @author Rossen Stoyanchev
* @since 4.2
*/
public interface UriTemplateHandler {
/**
* Expand the give URI template with a map of URI variables.
* @param uriTemplate the URI template string
* @param uriVariables the URI variables
* @return the resulting URI
*/
URI expand(String uriTemplate, Map<String, ?> uriVariables);
/**
* Expand the give URI template with an array of URI variable values.
* @param uriTemplate the URI template string
* @param uriVariableValues the URI variable values
* @return the resulting URI
*/
URI expand(String uriTemplate, Object... uriVariableValues);
}
......@@ -42,6 +42,7 @@ import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.converter.GenericHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.util.DefaultUriTemplateHandler;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
......@@ -259,6 +260,35 @@ public class RestTemplateTests {
verify(response).close();
}
@Test
public void getForObjectWithCustomUriTemplateHandler() throws Exception {
DefaultUriTemplateHandler uriTemplateHandler = new DefaultUriTemplateHandler();
uriTemplateHandler.setParsePath(true);
template.setUriTemplateHandler(uriTemplateHandler);
URI expectedUri = new URI("http://example.com/hotels/1/pic/pics%2Flogo.png/size/150x150");
given(requestFactory.createRequest(expectedUri, HttpMethod.GET)).willReturn(request);
given(request.getHeaders()).willReturn(new HttpHeaders());
given(request.execute()).willReturn(response);
given(errorHandler.hasError(response)).willReturn(false);
given(response.getStatusCode()).willReturn(HttpStatus.OK);
given(response.getHeaders()).willReturn(new HttpHeaders());
given(response.getBody()).willReturn(null);
Map<String, String> uriVariables = new HashMap<String, String>(2);
uriVariables.put("hotel", "1");
uriVariables.put("publicpath", "pics/logo.png");
uriVariables.put("scale", "150x150");
String url = "http://example.com/hotels/{hotel}/pic/{publicpath}/size/{scale}";
template.getForObject(url, String.class, uriVariables);
verify(response).close();
}
@Test
public void headForHeaders() throws Exception {
......
/*
* 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.util;
import static org.junit.Assert.assertEquals;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
/**
* Unit tests for {@link DefaultUriTemplateHandler}.
* @author Rossen Stoyanchev
*/
public class DefaultUriTemplateHandlerTests {
private DefaultUriTemplateHandler handler;
@Before
public void setUp() throws Exception {
this.handler = new DefaultUriTemplateHandler();
}
@Test
public void expandWithFullPath() throws Exception {
Map<String, String> vars = new HashMap<String, String>(2);
vars.put("hotel", "1");
vars.put("publicpath", "pics/logo.png");
String template = "http://example.com/hotels/{hotel}/pic/{publicpath}";
URI actual = this.handler.expand(template, vars);
URI expected = new URI("http://example.com/hotels/1/pic/pics/logo.png");
assertEquals("Invalid expanded template", expected, actual);
}
@Test
public void expandWithFullPathParsedIntoPathSegments() throws Exception {
Map<String, String> vars = new HashMap<String, String>(2);
vars.put("hotel", "1");
vars.put("publicpath", "pics/logo.png");
vars.put("scale", "150x150");
String template = "http://example.com/hotels/{hotel}/pic/{publicpath}/size/{scale}";
this.handler.setParsePath(true);
URI actual = this.handler.expand(template, vars);
URI expected = new URI("http://example.com/hotels/1/pic/pics%2Flogo.png/size/150x150");
assertEquals("Invalid expanded template", expected, actual);
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册