http请求功能的封装实现
闲来无事,看下一下项目上,大部分的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
上一篇: 分享5个可视化的正则表达式编辑工具