这是在项目中运用HttpURLConnection发送post请求(我们项目中是用来发送soap),完整的处理,包括如何处理异常。需要注意一个小地方,我们把创建URL放在afterPropertiesSet方法中,是因为这个类只是为下游某一个特定的Service服务的,如果要作用共用的,不能这样做。
import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URL;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.InitializingBean;
public class HttpClient implements InitializingBean
{
private static final String DEBUG_HTTP_RESULT = "Result of calling http service: ";
private static final String ERROR_DEFAULT_HTTP = "Failed to call HTTP Service at ";
private static final String ERROR_CONNECTION = "Failed to connect to ";
private static final String ERROR_TIMEOUT = "Timeout occured connecting to ";
private static final String ERROR_UNKNOWN_RESPONSE_CODE_RECIEVED = "Unknown Response Code %s recieved: ";
private static final String ERROR_INTERNAL_ERROR = "Internal Server Error: ";
private static final String ERROR_BAD_REQUEST = "Bad Request: ";
private static final String URL_FORMAT = "%s://%s:%s";
private static final String REQUEST_METHOD_POST = "POST";
private static final Logger LOGGER = Logger.getLogger(HttpClient.class);
private String m_hostname = "";
private String m_protocol = "";
private String m_port = "";
//default is set to 10 seconds
private int m_timeoutMs = 10000;
private URL endpoint = null;
/**
* Must be called after connection params are set
* @throws MalformedURLException
*/
public void afterPropertiesSet() throws MalformedURLException
{
endpoint = new URL(getHttpEndpoint());
}
public byte[] send(byte[] input, int numRetries) throws DataTransportException
{
byte[] payload = null;
for (int i=1; i<=numRetries; i++)
{
try
{
//create a new connection for each request
HttpURLConnection connection = (HttpURLConnection) endpoint.openConnection();
connection.setReadTimeout(m_timeoutMs);
connection.setConnectTimeout(m_timeoutMs);
connection.setRequestMethod(REQUEST_METHOD_POST);
connection.setDoInput(true);
connection.setDoOutput(true);
//write our request to the post body using an outputstream
DataOutputStream wr = new DataOutputStream(connection.getOutputStream());
wr.write(input);
wr.flush();
wr.close();
//check for success or failure
handleResponseCodes(connection);
//how big is our response
int length = connection.getContentLength();
LOGGER.debug("bytes received: " + length);
//read response
payload = readPayload(connection, length);
break;
}
catch (ConnectException ce)
{
hrowDataTransportException(..........)
}
catch (SocketTimeoutException ste)
{
throwDataTransportException(.....);
}
catch (Throwable t)
{
throwDataTransportException(i, numRetries, EventLogIds.HTTP_DEFAULT_ERROR,
ERROR_DEFAULT_HTTP + endpoint, t);
}
}
return payload;
}
/**
* Logs error message and throws exception
* @param errorCode
* @param errorMessage
* @param e
* @throws CommonHotelException
*/
private void throwDataTransportException(int currentAttempt, int totalAttempt, int errorCode,
String errorMessage, Throwable e)
throws DataTransportException
{
if (currentAttempt==totalAttempt)
{
throw new DataTransportException(errorMessage, errorCode, e);
}
else
{
String message = String.format("Http Client attempt %d of %d failed: %s",
currentAttempt, totalAttempt, errorMessage);
EventLogEntry.logEvent(LOGGER, Level.WARN, errorCode, message, e);
}
}
/**
* Checks http error code and message
* @param errorCode
* @param errorMessage
* @param e
* @throws IOException
* @throws CommonHotelException
*/
private void throwDataTransportException(int errorCode, String errorMessage, Throwable e, InputStream errorStream)
throws DataTransportException, IOException
{
//try to read from stderr
StringBuilder builder = new StringBuilder(errorMessage);
builder.append(". Details: ");
if(errorStream != null)
{
BufferedReader in = null;
try
{
in = new BufferedReader(new InputStreamReader(errorStream));
String line = in.readLine();
while(line != null)
{
builder.append(line);
line = in.readLine();
}
}
catch (IOException e1)
{
LOGGER.warn(Utilities.getExceptionStackTrace(e1));
}
finally
{
if(in != null)
{
in.close();
}
}
}
throw new DataTransportException(builder.toString(), errorCode, e);
}
private void handleResponseCodes(HttpURLConnection connection)
throws DataTransportException, IOException
{
int responseCode = connection.getResponseCode();
String responseMessage = connection.getResponseMessage();
InputStream errorStream = connection.getErrorStream();
switch (responseCode)
{
case HttpURLConnection.HTTP_OK:
EventLogEntry.logEvent(LOGGER, Level.DEBUG,
EventLogIds.HTTP_TRANSFER_SUCCESS, DEBUG_HTTP_RESULT
+ responseMessage);
break;
case HttpURLConnection.HTTP_BAD_REQUEST:
throwDataTransportException(EventLogIds.HTTP_BAD_REQUEST, ERROR_BAD_REQUEST
+ responseMessage, null, errorStream);
case HttpURLConnection.HTTP_INTERNAL_ERROR:
throwDataTransportException(EventLogIds.HTTP_INTERNAL_SERVER_ERROR,
ERROR_INTERNAL_ERROR + responseMessage, null, errorStream);
default:
String message = String.format(ERROR_UNKNOWN_RESPONSE_CODE_RECIEVED, responseCode);
throwDataTransportException(EventLogIds.HTTP_DEFAULT_ERROR,
message + responseMessage, null, errorStream);
}
}
/**
* reads all bytes in the response
*
* @param connection
* @param length
* must specify length of expected payload
* @throws IOException
*/
private byte[] readPayload(HttpURLConnection connection, int length) throws IOException
{
byte[] payload = new byte[length];
//read all bytes
//TODO: need a way to break loop in error cases
InputStream is = null;
try{
is = connection.getInputStream();
int bytesRemaining = length;
while( bytesRemaining > 0)
{
int off = length - bytesRemaining;
int avail = is.available();
int bytesRead = is.read(payload, off, avail);
bytesRemaining -= bytesRead;
}
}
finally{
if(is != null)
{
is.close();
}
}
return payload;
}
private String getHttpEndpoint()
{
return String.format(URL_FORMAT, m_protocol, m_hostname, m_port);
}
/**
* Set hostname we are sending to
* @param hostname
*/
public void setHostname(String hostname)
{
m_hostname = hostname;
}
/**
* Set protocol. eg. "http"
* @param protocol
*/
public void setProtocol(String protocol)
{
m_protocol = protocol;
}
/**
* Set port
* @param port
*/
public void setPort(String port)
{
m_port = port;
}
public void setTimeoutMs(int timeoutMs)
{
m_timeoutMs = timeoutMs;
}
}