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.
This is simple????