The REST client configuration creates a RestTemplate
and leverages the same marshallers that the server uses.
The default configuration uses the JAXB marshaller, but if the
Spring Profile for JSON is activated the JSON marshaller will be used for
client requests.
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <import resource="classpath:/META-INF/spring/marshaller/jaxb2-marshaller-context.xml"/> <context:component-scan base-package="org.springbyexample.contact.web.client" /> <bean id="httpClient" class="org.apache.http.impl.client.DefaultHttpClient"> <constructor-arg> <bean class="org.apache.http.impl.conn.PoolingClientConnectionManager"/> </constructor-arg> </bean> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate" p:messageConverters-ref="messageConvertersList"> <constructor-arg> <bean class="org.springframework.http.client.HttpComponentsClientHttpRequestFactory"> <constructor-arg ref="httpClient"/> </bean> </constructor-arg> </bean> <util:list id="messageConvertersList"> <bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter" p:supportedMediaTypes="application/xml"> <property name="marshaller" ref="marshaller" /> <property name="unmarshaller" ref="marshaller" /> </bean> </util:list> <beans profile="rest-json"> <import resource="classpath:/META-INF/spring/mvc/rest-json-converter-context.xml"/> <import resource="classpath:/META-INF/spring/mvc/rest-json-type-converter-context.xml"/> <!-- since id is the same as XML list above, essentially overriding the other list --> <util:list id="messageConvertersList"> <ref bean="mappingJacksonHttpMessageConverterWithType"/> </util:list> </beans> </beans>
Example 5. RestClient
in Spring by Example REST Module
The RestClient
configures the RestTemplate
with the default credentials, and also can create URLs from a URI.
@Component public class RestClient { final Logger logger = LoggerFactory.getLogger(getClass()); private final RestTemplate template; private final RestClientProperties clientProperties; private final DefaultHttpClient httpClient; @Autowired public RestClient(RestTemplate template, RestClientProperties clientProperties, DefaultHttpClient httpClient) { this.template = template; this.clientProperties = clientProperties; this.httpClient = httpClient; } @PostConstruct public void init() { setCredentials(clientProperties.getUsername(), clientProperties.getPassword()); } /** * Gets rest template. */ public RestTemplate getRestTemplate() { return template; } /** * Creates URL based on the URI passed in. */ public String createUrl(String uri) { StringBuilder sb = new StringBuilder(); sb.append(clientProperties.getUrl()); sb.append(clientProperties.getApiPath()); sb.append(uri); logger.debug("URL is '{}'.", sb.toString()); return sb.toString(); } /** * Set default credentials on HTTP client. */ public void setCredentials(String userName, String password) { UsernamePasswordCredentials creds = new UsernamePasswordCredentials(clientProperties.getUsername(), clientProperties.getPassword()); AuthScope authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM); httpClient.getCredentialsProvider().setCredentials(authScope, creds); } }
Example 6. AbstractPersistenceFindClient
Abstract persistence client base for read-only operations.
public abstract class AbstractPersistenceFindClient<R extends EntityResponseResult, FR extends EntityFindResponseResult> implements PersistenceFindMarshallingService<R, FR> { final Logger logger = LoggerFactory.getLogger(getClass()); protected final RestClient client; private final String findByIdRequest; private final String findPaginatedRequest; private final String findRequest; protected final Class<R> responseClazz; protected final Class<FR> findResponseClazz; public AbstractPersistenceFindClient(RestClient client, String findByIdRequest, String findPaginatedRequest, String findRequest, Class<R> responseClazz, Class<FR> findResponseClazz) { this.client = client; this.findByIdRequest = findByIdRequest; this.findPaginatedRequest = findPaginatedRequest; this.findRequest = findRequest; this.responseClazz = responseClazz; this.findResponseClazz = findResponseClazz; } @Override public R findById(Integer id) { R response = null; String url = client.createUrl(findByIdRequest); logger.debug("REST client findById. id={} url='{}'", id, url); response = client.getRestTemplate().getForObject(url, responseClazz, createPkVars(id)); return response; } @Override public FR find(int page, int pageSize) { FR response = null; String url = client.createUrl(findPaginatedRequest); logger.debug("REST client paginated find. page={} pageSize={} url='{}'", new Object[] { page, pageSize, url}); response = client.getRestTemplate().getForObject(url, findResponseClazz, createPageVars(page, pageSize)); return response; } @Override public FR find() { FR response = null; String url = client.createUrl(findRequest); logger.debug("REST client find. url='{}'", url); response = client.getRestTemplate().getForObject(url, findResponseClazz); return response; } /** * Create primary key request variables. */ public Map<String, Long> createPkVars(long id) { return Collections.singletonMap(ID_VAR, id); } /** * Create page vars for a paginated request. */ public Map<String, Integer> createPageVars(int page, int pageSize) { Map<String, Integer> result = new HashMap<String, Integer>(); result.put(PAGE_VAR, page); result.put(PAGE_SIZE_VAR, pageSize); return result; } }
Example 7. AbstractPersistenceClient
Abstract persistence client base for persistent operations.
public abstract class AbstractPersistenceClient<R extends EntityResponseResult, FR extends EntityFindResponseResult, S extends PkEntityBase> extends AbstractPersistenceFindClient<R, FR> implements PersistenceMarshallingService<R, FR, S> { private final String saveRequest; private final String updateRequest; private final String deletePkRequest; private final String deleteRequest; public AbstractPersistenceClient(RestClient client, String findByIdRequest, String findPaginatedRequest, String findRequest, String saveRequest, String updateRequest, String deletePkRequest, String deleteRequest, Class<R> responseClazz, Class<FR> findResponseClazz) { super(client, findByIdRequest, findPaginatedRequest, findRequest, responseClazz, findResponseClazz); this.saveRequest = saveRequest; this.updateRequest = updateRequest; this.deletePkRequest = deletePkRequest; this.deleteRequest = deleteRequest; } @Override public R create(S request) { R response = null; String url = client.createUrl(saveRequest); logger.debug("REST client save. id={} url='{}'", request.getId(), url); response = client.getRestTemplate().postForObject(url, request, responseClazz); return response; } @Override public R update(S request) { R response = null; String url = client.createUrl(updateRequest); logger.debug("REST client update. id={} url='{}'", request.getId(), url); Map<String, Long> vars = createPkVars(request.getId()); response = client.getRestTemplate().exchange(url, HttpMethod.PUT, new HttpEntity(request), responseClazz, vars).getBody(); return response; } public R delete(Integer id) { R response = null; String url = client.createUrl(deletePkRequest); logger.debug("REST client delete. id={} url='{}'", id, url); Map<String, Long> vars = createPkVars(id); response = client.getRestTemplate().exchange(url, HttpMethod.DELETE, null, responseClazz, vars).getBody(); return response; } @Override public R delete(S request) { throw new UnsupportedOperationException("Issue with DELETE and posting body."); ... } }
Example 8. PersonClient
The abstract base classes handle everything once all the URIs and expected responses are correctly set in the constructor.
@Component public class PersonClient extends AbstractPersistenceClient<PersonResponse, PersonFindResponse, Person> implements PersonMarshallingService { @Autowired public PersonClient(RestClient client) { super(client, FIND_BY_ID_REQUEST, FIND_PAGINATED_REQUEST, FIND_REQUEST, SAVE_REQUEST, UPDATE_REQUEST, DELETE_PK_REQUEST, DELETE_REQUEST, PersonResponse.class, PersonFindResponse.class); } }