闲来无事,看下一下项目上,大部分的http请求类,发现。。都是一个方法,最后得到一个String,一般的方法都是,getStringForHttp(url),getJsonForHttp。那么在这样的方法上,我想实现一个getByteForHttp的话,就只能重新复制一份代码,然后再最后的io操作,再另外处理。
但。。这叫什么面向对象,所以抽了点时间,写了个http的请求项目,暂时没测试出什么问题。希望有需要的,可以拿过去用,及时反馈bug。
1.http请求主流程走向:
/**
* @author lyen.wu
* http请求主流程
*/
public interface IYtBaseConnection {
/**
* 设置request的参数
* @param connectParam
*/
public void setConnectParam(IYtBaseConnectParam connectParam);
/**设置url,自动生成baseConnectParam*/
public void setConnectParam(String url);
/**
* 获取返回的结果对象
* */
public IYtBaseConnectResult getConnectResult() ;
public void post() throws Exception;
public void get() throws Exception;
/**
* 获取结果对象的简略结果,比如string,比如filePath
* */
public Object getResultObj();
}
2.封装请求参数
/**
* @author lyen.wu
* urlConnect请求的参数
*/
public interface IYtBaseConnectParam {
/**
* 获取url链接
* */
public String getUrl() ;
/**
* 获取property属性
* */
public Map<String, String> getPropertyMap();
/**
* 设置property属性
* */
public void setPropertyMap( Map<String, String> propertyMap);
/**
* 获取请求参数
* */
public String getParam();
/**获取编码*/
public String getCharsetCode() ;
/**设置cookieMap,每次请求完成之后,都会将cookie设置进来*/
public void setCookieMap(Map<String, String> cookieMap);
/**获取cookieMap*/
public Map<String, String> getCookieMap();
/**302自动跳转*/
public boolean isAutoRedirect();
/**
* 最终是以byte的方法发送请求参数
* */
public byte[] getParamByte();
}
3.封装请求的结果
/**
* @author lyen.wu
* http请求的结果类
*/
public interface IYtBaseConnectResult {
/**设置状态码*/
public void setStateCode(int stateCode);
/**设置inputstream*/
public void setIn(InputStream in);
/**获取状态码*/
public int getStateCode() ;
/**获取http请求得到的inputstream*/
public InputStream getIn();
/**处理inputream的流*/
public void dealIn()throws Exception;
/**处理inputream的流*/
public void dealIn(String charset) throws Exception;
}
至此这个http的骨架已经基本完善,剩下的就是为这个骨架贴肉。
4.首先是主流程的肉
package com.yt.tools.http.connection;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Map;
import com.yt.tools.http.connection.call.AbstractCall;
import com.yt.tools.http.connection.call.SimpleCall;
import com.yt.tools.http.connection.face.IYtBaseConnection;
import com.yt.tools.http.param.YtBaseConnectParam;
import com.yt.tools.http.param.face.IYtBaseConnectParam;
import com.yt.tools.http.result.face.IYtBaseConnectResult;
import com.yt.tools.http.utils.propety.YtHttpProtetyUtils;
import com.yt.tools.log.IYtConnLog;
import com.yt.tools.log.YtConnLogFactory;
/**
* 基础的网络请求类,封装了总体流程
*
* @author lyen.wu
*
*/
public abstract class YtBaseConnection implements IYtBaseConnection {
private static IYtConnLog log = null;
protected IYtBaseConnectResult connectResult;
protected IYtBaseConnectParam connectParam;
private AbstractCall call;
public YtBaseConnection() {
log = YtConnLogFactory.getLogger(this.getClass());
}
@Override
public void setConnectParam(IYtBaseConnectParam connectParam) {
this.connectParam = connectParam;
}
@Override
public void setConnectParam(String url) {
setConnectParam(new YtBaseConnectParam(url));
}
@Override
public void post() throws Exception {
getOrPost(getCall(), "post");
}
@Override
public void get() throws Exception {
getOrPost(getCall(), "get");
}
protected void getOrPost(AbstractCall call, String type) throws Exception {
connectResult = getConnectResult();
InputStream in = null;
try {
HttpURLConnection conn = getConnByConnectParm();
// 建立实际的连接
if (type.equals("get")) {
call.doGet(conn);
} else if (type.equals("post")) {
call.doPost(conn, connectParam.getParamByte());
}
// 设置流
in = conn.getInputStream();
setResult(conn);
// 定义BufferedReader输入流来读取URL的响应
String cookieValue = conn.getHeaderField("Set-Cookie");
YtHttpProtetyUtils.setCookie(connectParam.getCookieMap(),
cookieValue);
conn.disconnect();
} catch (Exception e) {
log.error("发送 " + type + " 请求出现异常!" + e);
throw e;
}
// 使用finally块来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
call.finallyAction();
afterRequest();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
/**
* 复写该方法,在请求之后的finally释放那里执行
*/
protected void afterRequest() {
}
public AbstractCall getCall() {
if (call == null) {
call = new SimpleCall();
}
return call;
}
public void setCall(AbstractCall call) {
this.call = call;
}
/**
* 设置请求结果
* @param result
* @param conn
* @throws Exception
*/
protected void setResult( HttpURLConnection conn) throws Exception {
// 设置请求结果
int state = conn.getResponseCode();
connectResult.setIn(conn.getInputStream());
connectResult.setStateCode(state);
// 处理流
connectResult.dealIn(connectParam.getCharsetCode());
}
/**
* 设置conn的property属性
* @param conn
*/
protected void setPropertyMap(HttpURLConnection conn){
if (connectParam.getPropertyMap() == null
|| connectParam.getPropertyMap().size() == 0) {
connectParam.setPropertyMap(YtHttpProtetyUtils
.getCommonProtety());
}
Map<String, String> map = connectParam.getPropertyMap();
// 设置通用的请求属性
YtHttpProtetyUtils.setRequestProperty(conn, map);
// 设置cookie
YtHttpProtetyUtils.setRequestProperty(conn,
connectParam.getCookieMap());
}
/**
* 根据connectParam生成HttpURLConnection
* @return
* @throws Exception
*/
public HttpURLConnection getConnByConnectParm() throws Exception{
URL realUrl = new URL(connectParam.getUrl());
// 打开和URL之间的连接
HttpURLConnection conn = (HttpURLConnection) realUrl
.openConnection();
// 设置302自动跳转
conn.setInstanceFollowRedirects(connectParam.isAutoRedirect());
setPropertyMap(conn);
return conn;
}
}
此主流程只是封装了正常的http请求功能,但是并没有处理io流的功能,io流的处理,应该交由子实现类去实现,因为有的功能需要图片,有的功能需要byte,有的功能需要String,这时候,应该暴露适当的流程空间交由外部去实现。
5.先补一个param的实现类:
/**
* urlConnect请求的参数
*
* @author lyen.wu
*
*/
public class YtBaseConnectParam implements IYtBaseConnectParam{
//表头参数设置
private Map<String, String> propertyMap = new HashMap<String, String>();
//get或者post的参数
private String param = "";
//byte数组的请求参数,param会转成paramByte
private byte[] paramByte = null;
//请求的链接
private String url = "";
// 设置 HttpURLConnection的字符编码
private String charsetCode = "UTF-8";
//cookie用以共用session
private Map<String, String> cookieMap = new HashMap<String, String>();
private boolean autoRedirect = true;
public YtBaseConnectParam(String url, String param) {
this(url,null,null,param);
}
public YtBaseConnectParam(String url) {
this(url,"");
}
public YtBaseConnectParam(String url, Map<String, String> propertiesMap,
Map<String, String> headerMap, String param) {
super();
this.url = url;
if(propertiesMap != null){
this.propertyMap = propertiesMap;
}
this.param = param;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
/**
* 设置通用的property属性
*/
public Map<String, String> setCommonProperty() {
propertyMap = YtHttpProtetyUtils.getCommonProtety();
return propertyMap;
}
@Override
public String toString() {
return "ConnectParam [propertiesMap=" + propertyMap + ", param="
+ param + ", url=" + url + "]";
}
public Map<String, String> getPropertyMap() {
return propertyMap;
}
public void setPropertyMap(Map<String, String> propertiesMap) {
this.propertyMap = propertiesMap;
}
public String getParam() {
return param;
}
public void setParam(String param) {
this.param = param;
}
public String getCharsetCode() {
return charsetCode;
}
public void setCharsetCode(String charsetCode) {
this.charsetCode = charsetCode;
}
public Map<String, String> getCookieMap() {
return cookieMap;
}
public void setCookieMap(Map<String, String> cookieMap) {
this.cookieMap = cookieMap;
}
/**
* 设置cookie内容,会自动转成map
* 第一个属性同时会存入,only-sessionId
* @param cookieContent
*/
public void setCookie(String cookieContent){
if(cookieContent == null){
return ;
}
String[] cookies = cookieContent.split(";");
for( int i = 0 ; i < cookies.length ; i ++ ){
String[] cookie = cookies[i].split("=");
String key = cookie[0];
String value = "";
if(cookie.length > 1){
value = cookie[1];
}
cookieMap.put(key , value);
if(i == 0){
cookieMap.put("only-sessionId", value);
}
}
}
public boolean isAutoRedirect() {
return autoRedirect;
}
public void setAutoRedirect(boolean autoRedirect) {
this.autoRedirect = autoRedirect;
}
public byte[] getParamByte() {
if(paramByte == null){
try {
paramByte = param.getBytes(getCharsetCode());
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return paramByte;
}
public void setParamByte(byte[] paramByte) {
this.paramByte = paramByte;
}
}
比如常用的获取字符串功能。
YtStringConnection.java
package com.yt.tools.http.connection;
import com.yt.tools.http.result.YtBaseConnectResult;
import com.yt.tools.http.result.YtStringConnectResult;
/**
* 文本网络请求,获取网页的内容或者回包
* @author lyen.wu
*
*/
public class YtStringConnection extends YtBaseConnection{
private YtStringConnectResult stringConnectResult = null ;
@Override
public YtBaseConnectResult getConnectResult() {
if(stringConnectResult == null){
stringConnectResult = new YtStringConnectResult();
}
return stringConnectResult;
}
@Override
public Object getResultObj() {
return stringConnectResult.getContent();
}
}
result结果类:
/**
* 文本回包
* @author lyen.wu
*
*/
public class YtStringConnectResult extends YtBaseConnectResult {
private String content = "";
@Override
public void dealIn(String charsetCode) throws Exception {
content = YtInputStream2OtherUtils.in2String(in, charsetCode);
}
public String getContent(){
return content;
}
}
又比如文件获取类:
/**
* 获取文件的网络请求,保存到本地。
* @author lyen.wu
*
*/
public class YtFileConnection extends YtBaseConnection{
private YtFileConnectResult fileConnectResult = new YtFileConnectResult();
public YtFileConnection(String filePath) {
fileConnectResult.setFilePath(filePath);
}
@Override
public YtBaseConnectResult getConnectResult() {
return fileConnectResult;
}
@Override
public Object getResultObj() {
return fileConnectResult.getFilePath();
}
}
/**
* 转换成file
*
* @author lyen.wu
*
*/
public class YtFileConnectResult extends YtBaseConnectResult {
/** 文件存放路径 */
private String filePath = "";
@Override
public void dealIn(String charset) throws Exception {
YtInputStream2OtherUtils.in2File(in, filePath);
}
public String getFilePath() {
return filePath;
}
public void setFilePath(String filePath) {
this.filePath = filePath;
}
}
流程就已经完成,剩下缺少的类就是一些工具类。
public class YtHttpProtetyUtils {
public static String defaultCode = "UTF-8";
/**
* 获取通用的requestPropertity设置
*
* @return
*/
public static Map<String, String> getCommonProtety() {
Map<String, String> map = new HashMap<String, String>();
map.put("accept", "*/*");
map.put("connection", "Keep-Alive");
map.put("user-agent",
"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");
map.put("Content-Type",ContentTypeEnum.APP_DEFAULT.getValue());
return map;
}
/**
* 设置request属性
*
* @param conn
* @param map
*/
public static void setRequestProperty(HttpURLConnection conn,
Map<String, String> map) {
if (map == null) {
return;
}
for (String key : map.keySet()) {
conn.setRequestProperty(key, map.get(key));
}
}
/**
* 设置cookie内容,会自动转成map
* 第一个属性同时会存入,only-sessionId
* @param cookieContent
*/
public static void setCookie(Map<String,String> cookieMap, String cookieContent){
if(cookieContent == null){
return ;
}
String[] cookies = cookieContent.split(";");
for( int i = 0 ; i < cookies.length ; i ++ ){
String[] cookie = cookies[i].split("=");
String key = cookie[0];
String value = "";
if(cookie.length > 1){
value = cookie[1];
}
cookieMap.put(key , value);
if(i == 0){
cookieMap.put("only-sessionId", value);
}
}
}
}
/**
* Content-Type中的常用请求枚举
* @author lyen.wu
* email:flash_rui@126.com
* 2017-12-14
*/
public enum ContentTypeEnum {
JSON("application/json; charset="+YtHttpProtetyUtils.defaultCode),TEXT_XML("text/xml; charset="+YtHttpProtetyUtils.defaultCode),
TEXT_HTML("text/html; charset="+YtHttpProtetyUtils.defaultCode),
APP_DEFAULT("application/x-www-form-urlencoded; charset="+YtHttpProtetyUtils.defaultCode),
TEXT_PLAIN("text/plain; charset="+YtHttpProtetyUtils.defaultCode);
private String value;
private ContentTypeEnum(String value){
this.value = value;
}
public String getValue() {
return value;
}
public void setValue(String value) {
this.value = value;
}
}
/**
* inputStream流处理方法
* @author lyen.wu
* email:flash_rui@126.com
* 2017-7-17
*/
public class YtInputStream2OtherUtils {
/**
* in流转string
* @param in
* @param charsetCode
* @return
* @throws Exception
*/
public static String in2String(InputStream in,String charsetCode) throws Exception{
BufferedReader br = null;
try{
String result = "";
br = new BufferedReader(new InputStreamReader(in,
charsetCode ));
String line;
while ((line = br.readLine()) != null) {
result += line + "\n";
}
return result;
}catch(Exception e){
throw e;
}finally{
//当br关闭,in也会关闭,不过还是一个个的来
if(br != null){
br.close();
}
if(in != null){
in.close();
}
}
}
/**
* in流转文件,且自动关闭
* @param inputStream
* @param filePath
* @throws Exception
*/
public static void in2File(InputStream inputStream , String filePath ) throws Exception{
in2File(inputStream,filePath,true);
}
/**
* in流转File,参数决定是否关闭in流
* @param inputStream
* @param filePath
* @param autoClose 自动关闭in流
* @throws Exception
*/
public static void in2File(InputStream inputStream , String filePath , boolean autoClose) throws Exception{
byte[] data = new byte[1024];
int len = 0;
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = new FileOutputStream(filePath);
while ((len = inputStream.read(data)) != -1) {
fileOutputStream.write(data, 0, len);
}
} catch (Exception e) {
throw e;
} finally {
if (autoClose && inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
/**
* 从输入流获取数据
* @param inputStream
* @return
* @throws Exception
*/
public static byte[] in2Byte(InputStream inputStream) throws Exception{
byte[] buffer = new byte[1024];
int len = -1;
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
while((len = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0, len);
}
outputStream.close();
inputStream.close();
return outputStream.toByteArray();
}
}
Demo 请求样例代码:
@Test
public void testPost() throws Exception{
//需要post的参数
String param = "param=1";
//创建一个请求参数的param
YtBaseConnectParam connectParam = new YtBaseConnectParam(
"http://localhost:8080/test/temp1.json");
connectParam.setParam(param);
//创建一个标准的字符串结果请求
IYtBaseConnection connection = new YtStringConnection();
//设置请求baseparam
connection.setConnectParam(connectParam);
//post
connection.post();
//返回结果
log.info(connection.getResultObj() + "");
}
@Test
public void testImgGet() throws Exception {
//设置http请求的流程且具体实现类是ytFileConnection
IYtBaseConnection connection = new YtFileConnection("d:\\da.jpg");
//设置请求地址
connection.setConnectParam("http://pic.qiantucdn.com/58pic/15/28/06/57458PIC5Zb_1024.jpg!/fw/780/watermark/url/L3dhdGVybWFyay12MS4zLnBuZw==/align/center");
//get
connection.get();
//得到保存的地址
log.info(connection.getResultObj() + "");
}
@Test
public void testStringGet() throws Exception {
//设置http请求的流程且具体实现类是YtStringConnection
IYtBaseConnection connection = new YtStringConnection();
//设置请求地址
connection.setConnectParam("http://www.baidu.com");
//get
connection.get();
//得到这个网页的源码
log.info(connection.getResultObj() + "");
}
@Test
public void testJSONPost() throws Exception {
//json参数
String param = "{\"order\": \"32221\",\"refoundAmt\":\"9.5\"}";
YtBaseConnectParam connectParam = new YtBaseConnectParam(
"http://localhost:8080/pay/refund.json");
connectParam.setParam(param);
//此接口需要设置content-type方能解析。
connectParam.setCommonProperty().put("Content-Type", ContentTypeEnum.JSON.getValue());
IYtBaseConnection connection = new YtStringConnection();
connection.setConnectParam(connectParam);
connection.post();
log.info(connection.getResultObj() + "");
}
至此http请求功能完成。
但是可以看出,我们要请求一个url却要写那么一大堆代码,也是很不合理的,所以,封装可以封装的代码,再重构一下,此时就是外观模式的实现了:
YtHttpContextUtils:
public class YtHttpContextUtils {
/**
* 获取String
*
* @param url
* @return
* @throws Exception
*/
public static String getString(String url) throws Exception {
// 设置http请求的流程且具体实现类是YtStringConnection
IYtBaseConnection connection = new YtStringConnection();
// 设置请求地址
connection.setConnectParam(url);
// get
connection.get();
// 得到这个网页的源码
return connection.getResultObj() + "";
}
/**
* 请求网络文件,并保存到本地
*
* @param url
* @param localPath
* @throws Exception
*/
public static void getFile(String url, String localPath) throws Exception {
// 设置http请求的流程且具体实现类是ytFileConnection
IYtBaseConnection connection = new YtFileConnection(localPath);
// 设置请求地址
connection.setConnectParam(url);
// get
connection.get();
}
/**
* post json方式请求url
*
* @param url
* @param param
* json字符串
* @return
* @throws Exception
*/
public static String postJson(String url, String param) throws Exception {
return post(url,param,ContentTypeEnum.JSON.getValue());
}
/**
* post String方式请求url
* @param url
* @param param 字符串
* @return
* @throws Exception
*/
public static String postString(String url, String param) throws Exception {
return post(url,param,ContentTypeEnum.APP_DEFAULT.getValue());
}
/**
* post请求,且要设置property
* @param url
* @param param
* @param propertyString
* @return
* @throws Exception
*/
public static String post(String url, String param , String propertyString) throws Exception {
// json参数
YtBaseConnectParam connectParam = new YtBaseConnectParam(url);
connectParam.setParam(param);
// 此接口需要设置content-type方能解析。
connectParam.setCommonProperty().put("Content-Type",propertyString);
IYtBaseConnection connection = new YtStringConnection();
connection.setConnectParam(connectParam);
connection.post();
return connection.getResultObj() + "";
}
}
此时的demo就变成了:
static IYtConnLog log = YtConnLogFactory.getLogger(Demo.class);
@Test
public void testPost() throws Exception{
//需要post的参数
String param = "param=1";
String url = "http://localhost:8080/test/temp1.json";
String content = YtHttpContextUtils.postString(url, param);
log.info(content);
}
@Test
public void testStringGet() throws Exception {
String url = "http://www.baidu.com";
String content = YtHttpContextUtils.getString(url);
log.info(content);
}
@Test
public void testJSONPost() throws Exception {
//json参数
String param = "{\"order\": \"32221\",\"refoundAmt\":\"9.5\"}";
String url = "http://localhost:8080/pay/refund.json";
String result = YtHttpContextUtils.postJson(url, param);
log.info(result);
}
@Test
public void testFileGet() throws Exception {
String url = "http://www.webxml.com.cn/files/WeatherWsHelp.pdf";
String localPath = "d:\\WeatherWsHelp.pdf";
YtHttpContextUtils.getFile(url, localPath);
}
git路径:https://github.com/JavaRui/com.yt.tools/tree/master/com.yt.tools.http