A Simple Java REST Client with Apache Commons HttpClient
I recently needed to build a RESTful web service client for my Java application. I wanted something simple and I had a particular set of requirements I wanted to code around (like being able to return the original request parameters along with the response) so the leaner the the actual client the better. After a little research I decided to go with a solution built on Apache Commons HttpClient which provides a rich set of abstractions for client side http interactions. I like HttpClient because it provides a higher and more functional level of abstraction than underlying java.net classes but at the same time allows you to code around whatever Http idioms you want (simple request/response, conversational services, data submission etc.) without alot of bridge code like annotations or library specific interface implementations.
Since I needed to both asynchronously POST data to a restful endpoint as well as perform GET request against another endpoint, I abstracted all of the parameter bundling and message sending into a single processRequest() method instead of implementing separate post/get code. I also standardized the return type to a Map containing the objects I needed for my app e.g. the request parameters, the actual response and the HTTP response code (REQUEST_PARAMS, RESPONSE_BODY and RESPONSE_STATUS respectively):
processRequest():
public Map<String, Object> processRequest(String serviceUrl,
String sendMethod, Map<String, String> params)
{
if (StringUtils.isBlank(serviceUrl)) {
throw new AiServiceException("Service URL is required");
}
// Essentially return a new HttpClient(), but can be pulled from Spring context
HttpClient httpclient = getHttpClient();
HttpMethod method = getHttpMethodFromString(sendMethod);//See Details Below
method.setPath(serviceUrl);
httpclient.getParams().setParameter("http.protocol.version",HttpVersion.HTTP_1_1);
httpclient.getParams().setParameter("http.socket.timeout", Integer.valueOf(responseTimeOut));
httpclient.getParams().setParameter("http.protocol.content-charset",charSet);
setRequestParams(method, params);
HashMap<String, Object> responseObject = new HashMap<String, Object>();
try {
int responseCode = httpclient.executeMethod(method);
String respBody=getResponseBody(method);// See details Below
responseObject.put(REQUEST_PARAMS, params);
responseObject.put(RESPONSE_BODY, respBody);
responseObject.put(RESPONSE_STATUS, responseCode);
return responseObject;
} catch (Exception e) {
logger.error("Error Sending REST Request [URL:"+serviceUrl+",METHOD:"+sendMethod+",PARAMS:"+params+"]", e);
throw new AiServiceException(e);
} finally {
method.releaseConnection();
}
}
To make this work I wrote a method parse an actual HttpMethod Object from a given string:
getHttpMethodFromString():
private HttpMethod getHttpMethodFromString(String methodString) {
if (StringUtils.isNotBlank(methodString)) {
org.springframework.http.HttpMethod parsedMethod = org.springframework.http.HttpMethod
.valueOf(methodString.toUpperCase());
switch (parsedMethod) {//Add other methods as needed PUT, DELETE etc.
case GET:
return new GetMethod();
case POST:
return new PostMethod();
default:
return new GetMethod();
}
}
return new GetMethod();
}
Finally I needed to make sure to read the entire response before returning it to caller:
getResponseBody():
protected String getResponseBody(HttpMethod method){
//Ensure we have read entire response body by reading from buffered stream
if(method!=null&& method.hasBeenUsed()){
BufferedReader in=null;
StringWriter stringOut= new StringWriter();
BufferedWriter dumpOut = new BufferedWriter(stringOut,8192);
try {
in=new BufferedReader(new InputStreamReader(method.getResponseBodyAsStream()));
String line = "";
while ((line = in.readLine()) != null) {
dumpOut.write(line);
dumpOut.newLine();
}
} catch (IOException e) {
logger.error("Error Reading Response Stream",e);
throw new AiServiceException(e);
}finally{
try {
dumpOut.flush();
dumpOut.close();
if(in!=null)
in.close();
} catch (IOException e) {
logger.warn("Error Closing Response Stream",e);
}
}
return StringEscapeUtils.unescapeHtml(stringOut.toString());
}
return null;
}
The newer Apache HttpComponents Project offers some evolutionary features over HttpClient like Plug-able authentication mechanisms, but the code shown above based on the legacy HttpClient libs works very well for my needs and is easily upgradeable should the need arise.