001/* 002 * Copyright 2012-2017 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.boot.test.web.client; 018 019import java.io.IOException; 020import java.io.OutputStream; 021import java.net.URI; 022import java.net.URISyntaxException; 023 024import org.springframework.boot.web.client.RootUriTemplateHandler; 025import org.springframework.http.client.ClientHttpRequest; 026import org.springframework.http.client.ClientHttpResponse; 027import org.springframework.http.client.support.HttpRequestWrapper; 028import org.springframework.mock.http.client.MockClientHttpRequest; 029import org.springframework.test.web.client.ExpectedCount; 030import org.springframework.test.web.client.MockRestServiceServer; 031import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder; 032import org.springframework.test.web.client.RequestExpectationManager; 033import org.springframework.test.web.client.RequestMatcher; 034import org.springframework.test.web.client.ResponseActions; 035import org.springframework.test.web.client.SimpleRequestExpectationManager; 036import org.springframework.util.Assert; 037import org.springframework.web.client.RestTemplate; 038import org.springframework.web.util.UriTemplateHandler; 039 040/** 041 * {@link RequestExpectationManager} that strips the specified root URI from the request 042 * before verification. Can be used to simply test declarations when all REST calls start 043 * the same way. For example: <pre class="code"> 044 * RestTemplate restTemplate = new RestTemplateBuilder().rootUri("http://example.com").build(); 045 * MockRestServiceServer server = RootUriRequestExpectationManager.bindTo(restTemplate); 046 * server.expect(requestTo("/hello")).andRespond(withSuccess()); 047 * restTemplate.getForEntity("/hello", String.class); 048 * </pre> 049 * 050 * @author Phillip Webb 051 * @since 1.4.0 052 * @see RootUriTemplateHandler 053 * @see #bindTo(RestTemplate) 054 * @see #forRestTemplate(RestTemplate, RequestExpectationManager) 055 */ 056public class RootUriRequestExpectationManager implements RequestExpectationManager { 057 058 private final String rootUri; 059 060 private final RequestExpectationManager expectationManager; 061 062 public RootUriRequestExpectationManager(String rootUri, 063 RequestExpectationManager expectationManager) { 064 Assert.notNull(rootUri, "RootUri must not be null"); 065 Assert.notNull(expectationManager, "ExpectationManager must not be null"); 066 this.rootUri = rootUri; 067 this.expectationManager = expectationManager; 068 } 069 070 @Override 071 public ResponseActions expectRequest(ExpectedCount count, 072 RequestMatcher requestMatcher) { 073 return this.expectationManager.expectRequest(count, requestMatcher); 074 } 075 076 @Override 077 public ClientHttpResponse validateRequest(ClientHttpRequest request) 078 throws IOException { 079 String uri = request.getURI().toString(); 080 if (uri.startsWith(this.rootUri)) { 081 request = replaceURI(request, uri.substring(this.rootUri.length())); 082 } 083 try { 084 return this.expectationManager.validateRequest(request); 085 } 086 catch (AssertionError ex) { 087 String message = ex.getMessage(); 088 String prefix = "Request URI expected:</"; 089 if (message != null && message.startsWith(prefix)) { 090 throw new AssertionError("Request URI expected:<" + this.rootUri 091 + message.substring(prefix.length() - 1)); 092 } 093 throw ex; 094 } 095 } 096 097 private ClientHttpRequest replaceURI(ClientHttpRequest request, 098 String replacementUri) { 099 URI uri; 100 try { 101 uri = new URI(replacementUri); 102 if (request instanceof MockClientHttpRequest) { 103 ((MockClientHttpRequest) request).setURI(uri); 104 return request; 105 } 106 return new ReplaceUriClientHttpRequest(uri, request); 107 } 108 catch (URISyntaxException ex) { 109 throw new IllegalStateException(ex); 110 } 111 } 112 113 @Override 114 public void verify() { 115 this.expectationManager.verify(); 116 } 117 118 @Override 119 public void reset() { 120 this.expectationManager.reset(); 121 } 122 123 /** 124 * Return a bound {@link MockRestServiceServer} for the given {@link RestTemplate}, 125 * configured with {@link RootUriRequestExpectationManager} when possible. 126 * @param restTemplate the source REST template 127 * @return a configured {@link MockRestServiceServer} 128 */ 129 public static MockRestServiceServer bindTo(RestTemplate restTemplate) { 130 return bindTo(restTemplate, new SimpleRequestExpectationManager()); 131 } 132 133 /** 134 * Return a bound {@link MockRestServiceServer} for the given {@link RestTemplate}, 135 * configured with {@link RootUriRequestExpectationManager} when possible. 136 * @param restTemplate the source REST template 137 * @param expectationManager the source {@link RequestExpectationManager} 138 * @return a configured {@link MockRestServiceServer} 139 */ 140 public static MockRestServiceServer bindTo(RestTemplate restTemplate, 141 RequestExpectationManager expectationManager) { 142 MockRestServiceServerBuilder builder = MockRestServiceServer.bindTo(restTemplate); 143 return builder.build(forRestTemplate(restTemplate, expectationManager)); 144 } 145 146 /** 147 * Return {@link RequestExpectationManager} to be used for binding with the specified 148 * {@link RestTemplate}. If the {@link RestTemplate} is using a 149 * {@link RootUriTemplateHandler} then a {@link RootUriRequestExpectationManager} is 150 * returned, otherwise the source manager is returned unchanged. 151 * @param restTemplate the source REST template 152 * @param expectationManager the source {@link RequestExpectationManager} 153 * @return a {@link RequestExpectationManager} to be bound to the template 154 */ 155 public static RequestExpectationManager forRestTemplate(RestTemplate restTemplate, 156 RequestExpectationManager expectationManager) { 157 Assert.notNull(restTemplate, "RestTemplate must not be null"); 158 UriTemplateHandler templateHandler = restTemplate.getUriTemplateHandler(); 159 if (templateHandler instanceof RootUriTemplateHandler) { 160 return new RootUriRequestExpectationManager( 161 ((RootUriTemplateHandler) templateHandler).getRootUri(), 162 expectationManager); 163 } 164 return expectationManager; 165 } 166 167 /** 168 * {@link ClientHttpRequest} wrapper to replace the request URI. 169 */ 170 private static class ReplaceUriClientHttpRequest extends HttpRequestWrapper 171 implements ClientHttpRequest { 172 173 private final URI uri; 174 175 ReplaceUriClientHttpRequest(URI uri, ClientHttpRequest request) { 176 super(request); 177 this.uri = uri; 178 } 179 180 @Override 181 public URI getURI() { 182 return this.uri; 183 } 184 185 @Override 186 public OutputStream getBody() throws IOException { 187 return getRequest().getBody(); 188 } 189 190 @Override 191 public ClientHttpResponse execute() throws IOException { 192 return getRequest().execute(); 193 } 194 195 @Override 196 public ClientHttpRequest getRequest() { 197 return (ClientHttpRequest) super.getRequest(); 198 } 199 200 } 201 202}