我们项目中可能会使用很多的http请求的库,比如URLConnection、ApacheClient、OkHttp库等。每个库的操作方式都不一样,这是一个巨大的困扰。如果需要换一个库,那需要改变的东西就很多。利用抽象的思想,将http请求的整个过程抽象出来,包括异常处理,以后我们只需要面向接口编程即可。
首先设计了http的模板类HttpTemplate,不同的实现类只需要实现此模板,http参数是最全的,基本上就代表了http请求所需要的全部参数。然后设计了简化操作的HttpClient、SmartHttpClient及结果自动转换为Bean的ConverterSmartHttpClient。如此设计的一个好处是接口与实现分离,系统内部可以无限优化HttpTemplate,但是面向终端用户的HttpClient-SmartHttpClient-ConverterSmartConvert几乎不用变化。继承和实现的树为:
最顶级的HttpTemplate为
package cn.zytx.common.http.base;
import cn.zytx.common.http.Method;
import cn.zytx.common.utils.ArrayListMultimap;
import java.io.IOException;
/**
* http请求的模板方法接口
* @author xiongshiyan at 2018/6/19
*/
public interface HttpTemplate<C>{
/**
* http请求的模板方法
*
* @param url URL
* @param method 请求方法
* @param contentType 请求体MIME类型
* @param contentCallback 处理请求体的
* @param headers headers
* @param connectTimeout 连接超时时间
* @param readTimeout 读取超时时间
* @param resultCharset 结果字符集
* @param includeHeaders 是否结果包含header
* @param resultCallback 结果处理器
* @param <R> 处理的结果
* @return 处理的结果
* @throws IOException IOException
*/
<R> R template(String url, Method method, String contentType, ContentCallback<C> contentCallback,
ArrayListMultimap<String, String> headers, int connectTimeout, int readTimeout,
String resultCharset, boolean includeHeaders, ResultCallback<R> resultCallback) throws IOException;
}
其中ContentCallback用于将数据发送出去、ResultCallback用于封装结果。
package cn.zytx.common.http.base;
import okhttp3.RequestBody;
import org.apache.http.HttpEntity;
import java.io.IOException;
import java.net.HttpURLConnection;
/**
* 写入body数据,属于一个回调方法
* @author xiongshiyan at 2018/6/7
* @param <CC> 处理的泛型,不同的实现方式有不同的写入方式
* @see HttpURLConnection#getOutputStream()
* @see org.apache.http.client.methods.HttpEntityEnclosingRequestBase#setEntity(HttpEntity)
* @see okhttp3.Request.Builder#method(String, RequestBody)
*/
@FunctionalInterface
public interface ContentCallback<CC> {
/**
* 往connection中写入数据
* @param cc 连接
* @throws IOException IOException
*/
void doWriteWith(CC cc) throws IOException;
}
package cn.zytx.common.http.base;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
/**
* 结果封装,更通用的封装方式,输入更通用,自己转化成需要的数据结构string、bytes、file...
* @author xiongshiyan at 2018/6/7
*/
@FunctionalInterface
public interface ResultCallback<R> {
/**
* 转换结果
*
* @param stream body,代表输入流,自己转化成需要的数据结构string、bytes、file...
* @param resultCharset 编码
* @param headers headers
* @return <R>
* @throws IOException IOException
* @see cn.zytx.common.http.smart.Response
*/
R convert(InputStream stream, String resultCharset, Map<String, List<String>> headers) throws IOException;
}
http接口类,主要提供get、post方法。实现类只需要实现最全参数的get和post方法,其他都是调用此方法的简化方法。
String get(String url, Map<String, String> params,Map<String,String> headers,int connectTimeout,int readTimeout , String resultCharset) throws IOException;
String post(String url, String body, String contentType,Map<String,String> headers,int connectTimeout,int readTimeout, String bodyCharset , String resultCharset) throws IOException;
方法都声明抛出IOException,可以代表java.net.SocketTimeoutException connect timed out/read time out。另外如果过程中发生了任何异常都会抛出HttpException,可以处理内部错误和服务器错误,通过code区分。
package cn.zytx.common.http.basic;
import cn.zytx.common.http.HttpException;
import cn.zytx.common.http.HttpUtil;
import cn.zytx.common.http.smart.SmartHttpClient;
import cn.zytx.common.utils.ArrayListMultimap;
import java.io.File;
import java.io.IOException;
import java.util.Map;
/**
* GET POST接口
* @author 熊诗言2017/11/24
* @see SmartHttpClient
* 针对Http超时和各种错误码分别处理
* 使用时,可以直接new实现类,也可以通过{@link cn.zytx.common.utils.SpringFactoriesLoader }获取,这样就不会与实现类绑定
*/
public interface HttpClient {
int CONNECT_TIMEOUT = 15000;
int READ_TIMEOUT = 15000;
String EXCEPTION_UNKOWN = "未知异常";
String DEFAULT_CHARSET = "UTF-8";
String FORM_URLENCODED = "application/x-www-form-urlencoded";
String JSON = "application/json";
String TEXT_XML = "text/xml";
String FORM_URLENCODED_WITH_DEFAULT_CHARSET = FORM_URLENCODED + ";charset=" + DEFAULT_CHARSET;
String TEXT_XML_WITH_DEFAULT_CHARSET = TEXT_XML + ";charset=" + DEFAULT_CHARSET;
String JSON_WITH_DEFAULT_CHARSET = JSON + ";charset=" + DEFAULT_CHARSET;
/**
*HTTP GET请求
* @param url URL,可以帶参数
* @param params 参数列表,可以为 null
* @param headers HTTP header 可以为 null
* @param connectTimeout 连接超时时间
* @param readTimeout 读超时时间
* @param resultCharset 返回编码
* @return 返回的内容
* @throws IOException 超时异常 {@link java.net.SocketTimeoutException connect timed out/read time out}
* @throws HttpException 非200请求异常或者其他异常抛出
*/
String get(String url, Map<String, String> params,Map<String,String> headers,int connectTimeout,int readTimeout , String resultCharset) throws IOException;
default String get(String url, Map<String, String> params,Map<String,String> headers,int connectTimeout,int readTimeout) throws IOException{
return get(url, params, headers, connectTimeout, readTimeout , DEFAULT_CHARSET);
}
default String get(String url, Map<String, String> params,Map<String,String> headers , String resultCharset) throws IOException{
return get(url,params,headers,CONNECT_TIMEOUT,READ_TIMEOUT,resultCharset);
}
default String get(String url, Map<String, String> params,Map<String,String> headers) throws IOException{
return get(url,params,headers,CONNECT_TIMEOUT,READ_TIMEOUT);
}
default String get(String url, Map<String, String> params,int connectTimeout,int readTimeout , String resultCharset) throws IOException{
return get(url,params,null,connectTimeout,readTimeout,resultCharset);
}
default String get(String url, Map<String, String> params,int connectTimeout,int readTimeout) throws IOException{
return get(url,params,null,connectTimeout,readTimeout);
}
default String get(String url,Map<String, String> params , String resultCharset) throws IOException{
return get(url,params,null,CONNECT_TIMEOUT,READ_TIMEOUT,resultCharset);
}
default String get(String url,Map<String, String> params) throws IOException{
return get(url,params,null,CONNECT_TIMEOUT,READ_TIMEOUT);
}
default String get(String url , String resultCharset) throws IOException{
return get(url,null,null,CONNECT_TIMEOUT,READ_TIMEOUT , resultCharset);
}
default String get(String url) throws IOException{
return get(url,null,null,CONNECT_TIMEOUT,READ_TIMEOUT);
}
/**
* HTTP POST
* @param url URL
* @param body 请求体
* @param contentType 请求体类型
* @param headers 头
* @param connectTimeout 连接超时时间
* @param readTimeout 读超时时间
* @param bodyCharset 请求体编码
* @param resultCharset 返回编码
* @return 请求返回
* @throws IOException 超时异常 {@link java.net.SocketTimeoutException connect timed out/read time out}
* @throws HttpException 非200请求异常或者其他异常抛出
*/
String post(String url, String body, String contentType,Map<String,String> headers,int connectTimeout,int readTimeout, String bodyCharset , String resultCharset) throws IOException;
default String post(String url, String body, String contentType,Map<String,String> headers,int connectTimeout,int readTimeout) throws IOException{
return post(url,body,contentType,headers,connectTimeout,readTimeout, DEFAULT_CHARSET,DEFAULT_CHARSET);
}
default String post(String url, String body, String contentType,Map<String,String> headers, String bodyCharset , String resultCharset) throws IOException{
return post(url,body,contentType,headers,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset);
}
default String post(String url, String body, String contentType,Map<String,String> headers) throws IOException{
return post(url,body,contentType,headers,CONNECT_TIMEOUT,READ_TIMEOUT);
}
default String post(String url, String body, String contentType,int connectTimeout,int readTimeout, String bodyCharset , String resultCharset) throws IOException{
return post(url,body,contentType,null,connectTimeout,readTimeout,bodyCharset,resultCharset);
}
default String post(String url, String body, String contentType,int connectTimeout,int readTimeout) throws IOException{
return post(url,body,contentType,null,connectTimeout,readTimeout);
}
/**
* @see HttpClient#post(String, String, String, Map, int, int, String, String)
*/
default String post(String url, String body, String contentType, String bodyCharset , String resultCharset) throws IOException{
return post(url,body,contentType,null,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset);
}
default String post(String url, String body, String contentType) throws IOException{
return post(url,body,contentType,null,CONNECT_TIMEOUT,READ_TIMEOUT);
}
default String postJson(String url, String body, String bodyCharset , String resultCharset) throws IOException{
return post(url,body,JSON_WITH_DEFAULT_CHARSET,null,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset);
}
default String postJson(String url, String body) throws IOException{
return post(url,body,JSON_WITH_DEFAULT_CHARSET,null,CONNECT_TIMEOUT,READ_TIMEOUT);
}
/**参数用 =和& 连接*/
default String post(String url, Map<String, String> params,Map<String,String> headers, String bodyCharset , String resultCharset) throws IOException{
return post(url, HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,headers,bodyCharset,resultCharset);
}
default String post(String url, Map<String, String> params,Map<String,String> headers) throws IOException{
return post(url, HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,headers);
}
default String post(String url, Map<String, String> params, String bodyCharset , String resultCharset) throws IOException{
return post(url,HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,null,bodyCharset,resultCharset);
}
default String post(String url, Map<String, String> params) throws IOException{
return post(url,HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,null);
}
/**
* 文件下载相关,下载为字节数组
*/
byte[] getAsBytes(String url , ArrayListMultimap<String,String> headers, int connectTimeout, int readTimeout) throws IOException;
default byte[] getAsBytes(String url,int connectTimeout,int readTimeout) throws IOException{
return getAsBytes(url , null , connectTimeout , readTimeout);
}
default byte[] getAsBytes(String url,ArrayListMultimap<String,String> headers) throws IOException{
return getAsBytes(url , headers , CONNECT_TIMEOUT , READ_TIMEOUT);
}
default byte[] getAsBytes(String url) throws IOException{
return getAsBytes(url , null , CONNECT_TIMEOUT , READ_TIMEOUT);
}
/**
* 文件下载相关,下载为文件
*/
File getAsFile(String url , ArrayListMultimap<String,String> headers , File file ,int connectTimeout,int readTimeout) throws IOException;
default File getAsFile(String url , File file ,int connectTimeout,int readTimeout) throws IOException{
return getAsFile(url , null , file , connectTimeout , readTimeout);
}
default File getAsFile(String url , ArrayListMultimap<String,String> headers , File file) throws IOException{
return getAsFile(url , headers , file , CONNECT_TIMEOUT , READ_TIMEOUT);
}
default File getAsFile(String url , File file) throws IOException{
return getAsFile(url , null , file , CONNECT_TIMEOUT , READ_TIMEOUT);
}
/**
* 上传文件
* @param url URL
* @param files 多个文件信息
*/
String upload(String url , ArrayListMultimap<String,String> headers, int connectTimeout , int readTimeout , String resultCharset , FormFile... files) throws IOException;
default String upload(String url , ArrayListMultimap<String,String> headers, int connectTimeout , int readTimeout , FormFile... files) throws IOException{
return upload(url, headers ,connectTimeout , readTimeout , DEFAULT_CHARSET , files);
}
default String upload(String url , ArrayListMultimap<String,String> headers , FormFile... files) throws IOException{
return upload(url, headers ,CONNECT_TIMEOUT , READ_TIMEOUT, DEFAULT_CHARSET , files);
}
default String upload(String url , int connectTimeout , int readTimeout , FormFile... files) throws IOException{
return upload(url, null ,connectTimeout , readTimeout , DEFAULT_CHARSET , files);
}
default String upload(String url , FormFile... files) throws IOException{
return upload(url, null , CONNECT_TIMEOUT , READ_TIMEOUT , DEFAULT_CHARSET , files);
}
/**
* 上传文件和key-value数据
* @param url URL
* @param files 多个文件信息
*/
String upload(String url , ArrayListMultimap<String , String> params , ArrayListMultimap<String,String> headers, int connectTimeout , int readTimeout , String resultCharset , FormFile... files) throws IOException;
default String upload(String url , ArrayListMultimap<String , String> params ,ArrayListMultimap<String,String> headers, int connectTimeout , int readTimeout , FormFile... files) throws IOException{
return upload(url, params ,headers ,connectTimeout , readTimeout , DEFAULT_CHARSET , files);
}
default String upload(String url , ArrayListMultimap<String , String> params ,ArrayListMultimap<String,String> headers , FormFile... files) throws IOException{
return upload(url, params ,headers ,CONNECT_TIMEOUT , READ_TIMEOUT, DEFAULT_CHARSET , files);
}
default String upload(String url , int connectTimeout , int readTimeout , ArrayListMultimap<String , String> params ,FormFile... files) throws IOException{
return upload(url, params ,null ,connectTimeout , readTimeout , DEFAULT_CHARSET , files);
}
default String upload(String url ,Map<String , String> params , FormFile... files) throws IOException{
ArrayListMultimap<String , String> multimap = ArrayListMultimap.fromMap(params);
return upload(url, multimap ,null , CONNECT_TIMEOUT , READ_TIMEOUT , DEFAULT_CHARSET , files);
}
}
FormFile代表文件上传类:
package cn.zytx.common.http.basic;
import java.io.*;
/**
* 上传文件表单数据
* @author xiongshiyan
*/
public class FormFile{
/**上传文件的数据 */
/*private byte[] data;*/
private InputStream inStream;
private long fileLen;
/**文件名称 */
private String filName;
/**请求参数名称 */
private String parameterName;
/**内容类型 */
private String contentType = "application/octet-stream";
public FormFile(String filName, byte[] data, String parameterName, String contentType){
this.inStream = new ByteArrayInputStream(data);
init(filName, data.length , parameterName, contentType);
}
public FormFile(File file, String parameterName, String contentType) {
try {
this.inStream = new FileInputStream(file);
init(file.getName() , file.length() , parameterName , contentType);
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
}
public FormFile(String fileName, InputStream inStream, long fileLen, String parameterName, String contentType){
this.inStream = inStream;
init(fileName , fileLen , parameterName , contentType);
}
private void init(String filName, long fileLen , String parameterName, String contentType) {
this.filName = filName;
this.parameterName = parameterName;
this.fileLen = fileLen;
if(contentType != null){
this.contentType = contentType;
}
}
public InputStream getInStream(){
return inStream;
}
public long getFileLen(){
return fileLen;
}
public String getFilName(){
return filName;
}
public void setFilName(String filName){
this.filName = filName;
}
public String getParameterName(){
return parameterName;
}
public void setParameterName(String parameterName){
this.parameterName = parameterName;
}
public String getContentType(){
return contentType;
}
public void setContentType(String contentType){
this.contentType = contentType;
}
}
Http请求异常类:
package cn.zytx.common.http;
/**
* 1.HTTP请求异常,包括http组件内部错误-1,服务器返回错误(错误码和错误信息)
* 2.超时异常由专门的IOException表达
* @author 熊诗言 2017/11/24
*/
public class HttpException extends RuntimeException{
private int responseCode = -1;
private String errorMessage = "error happens in client";
public HttpException(int responseCode,String errorMessage){
super(errorMessage);
this.responseCode = responseCode;
this.errorMessage = errorMessage;
}
public HttpException(String errorMessage){
super(errorMessage);
this.errorMessage = errorMessage;
}
public HttpException(Exception e){
super(e);
this.errorMessage = e.getMessage();
}
public HttpException(){
}
public int getResponseCode() {
return responseCode;
}
public String getErrorMessage() {
return errorMessage;
}
}
另外定义了一些工具类和枚举类,HttpUtil用于连接参数为“k1=v1&k2=v2”的形式,绑定一个静态的HttpClient接口和实现并暴露,调用者可以初始化其为其他的实现类。
package cn.zytx.common.http;
import cn.zytx.common.Editor;
import cn.zytx.common.http.basic.HttpClient;
import cn.zytx.common.http.basic.NativeHttpClient;
import cn.zytx.common.http.smart.NativeSmartHttpClient;
import cn.zytx.common.http.smart.SmartHttpClient;
import cn.zytx.common.utils.ArrayListMultimap;
import cn.zytx.common.utils.CharsetUtil;
import cn.zytx.common.utils.Joiner;
import java.io.*;
import java.net.URLEncoder;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* @author xiongshiyan at 2017/12/11
*/
public class HttpUtil {
private HttpUtil(){}
/**
* key1=value1&key2=value2,如果value=null 或者 size=0 返回 ""
* @param value 键值对
*/
public static String contactMap(Map<String, String> value){
if(null == value){return "";}
/*value.forEach((k,v)->{
try {
value.put(k,URLEncoder.encode(v, CharsetUtil.UTF_8));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
});*/
Editor<String> editor = (v)->{
try {
return URLEncoder.encode(v, CharsetUtil.UTF_8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
};
return Joiner.on("&").withKeyValueSeparator("=",editor).useForNull("").join(value);
/*try {
String params;
params = "";
Iterator<String> iterator = value.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next().toString();
params += key + "=" + URLEncoder.encode(value.get(key), "UTF8") + "&";
}
if(params.length() > 0){
params = params.substring(0, params.length() - 1);
}
return params;
} catch (UnsupportedEncodingException e){
e.printStackTrace();
return "";
}*/
}
/**
* key1=value1&key2=value2&key2=value3,如果value=null 或者 size=0 返回 ""
* @param value 键值对
*/
public static String contactMap(ArrayListMultimap<String, String> value){
if(null == value){return "";}
try {
String params;
params = "";
Iterator<String> iterator = value.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next();
List<String> vList = value.get(key);
int len = vList.size();
for (int i = 0; i < len; i++) {
params += key + "=" + URLEncoder.encode(vList.get(i), "UTF8") + "&";
}
}
if(params.length() > 0){
params = params.substring(0, params.length() - 1);
}
return params;
} catch (UnsupportedEncodingException e){
e.printStackTrace();
return "";
}
}
/**
* URL和参数
* @param actionName URL,可以包含?
* @param paramString 参数字符串,可以为"",null,k1=v1&k2=v2
* @return 连接后的字符串
*/
public static String contactUrlParams(String actionName, String paramString) {
String url = actionName;
if(!"".equals(paramString)) {
//如果包含?,則直接追加//不包含?,則用?追加
url = actionName.contains("?") ? actionName + "&" + paramString : actionName + "?" + paramString;
}
return url;
}
/**
* @see HttpUtil#contactMap(ArrayListMultimap)
* @see HttpUtil#contactUrlParams(String, String)
*/
public static String contactUrlParams(String actionName, ArrayListMultimap<String , String> params) {
Objects.requireNonNull(actionName);
return contactUrlParams(actionName , contactMap(params));
}
/**
* @see HttpUtil#contactMap(Map)
* @see HttpUtil#contactUrlParams(String, String)
*/
public static String contactUrlParams(String actionName, Map<String , String> params) {
Objects.requireNonNull(actionName);
return contactUrlParams(actionName , contactMap(params));
}
/**
* 静态绑定
*/
private static HttpClient BASIC_HTTP_CLIENT = new NativeHttpClient();
private static SmartHttpClient SMART_HTTP_CLIENT = new NativeSmartHttpClient();
public static HttpClient getBasicHttpClient() {
return BASIC_HTTP_CLIENT;
}
public static void setBasicHttpClient(HttpClient httpClient){BASIC_HTTP_CLIENT = httpClient;}
public static SmartHttpClient getSmartHttpClient() {
return SMART_HTTP_CLIENT;
}
public static void setSmartHttpClient(SmartHttpClient smartHttpClient){SMART_HTTP_CLIENT = smartHttpClient;}
}
/**
* Http方法枚举
* @author Looly
*/
public enum Method {
/**
* HTTP相关方法
*/
GET, POST, HEAD, OPTIONS, PUT, DELETE, TRACE, CONNECT, PATCH
}
在HttpClient的基础上再定义了一个SmartHttpClient,更好的模拟了http请求(主要可以设置一些header参数),参数也更加面向对象。
package cn.zytx.common.http.smart;
import cn.zytx.common.http.basic.HttpClient;
import cn.zytx.common.http.HttpException;
import java.io.File;
import java.io.IOException;
import java.util.Objects;
/**
* @author xiongshiyan at 2017/12/9
* 针对Http超时和各种错误码分别处理
* @see HttpClient
* 使用时,可以直接new实现类,也可以通过{@link cn.zytx.common.utils.SpringFactoriesLoader }获取,这样就不会与实现类绑定
*/
public interface SmartHttpClient extends HttpClient{
/**
* GET方法
* @param request 请求参数
* @return 响应
* @throws IOException 超时等IO异常
* @throws HttpException 404,500等或者其他异常都转换为该异常
*/
Response get(Request request) throws IOException;
/**
* POST方法
* @param request 请求参数
* @return 响应
* @throws IOException 超时等IO异常
* @throws HttpException 404,500等或者其他异常都转换为该异常
*/
Response post(Request request) throws IOException;
/**
* 下载为字节数组
* @param request 请求参数
* @return byte[]
* @throws IOException IOException
*/
byte[] getAsBytes(Request request) throws IOException;
/**
* 下载文件
* @param request 请求参数
* @return File 下载的文件
* @throws IOException IOException
*/
File getAsFile(Request request) throws IOException;
/**
* 文件上传
* @param request 请求参数
* @return Response
* @throws IOException IOException
*/
Response upload(Request request) throws IOException;
/**
* 对请求参数拦截处理 , 比如统一添加header , 参数加密 , 默认不处理
* @param request Request
* @return Request
*/
default Request beforeTemplate(Request request){
return Objects.requireNonNull(request);
}
/**
* 对返回结果拦截处理 , 比如统一解密 , 默认不处理
* @param response Response
* @return Response
*/
default Response afterTemplate(Response response){
return response;
}
}
其中Request和Response定义如下,可以视为一个JavaBean。
package cn.zytx.common.http.smart;
import cn.zytx.common.http.HttpUtil;
import cn.zytx.common.http.basic.FormFile;
import cn.zytx.common.utils.ArrayListMultimap;
import cn.zytx.common.utils.StrUtil;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
/**
* @author xiongshiyan at 2017/12/9
*/
public class Request {
/**
* 请求的URL
*/
private String url;
/**
* 请求参数,针对GET请求存在
*/
private ArrayListMultimap<String,String> params = new ArrayListMultimap<>();
/**
* 请求头
*/
private ArrayListMultimap<String,String> headers = new ArrayListMultimap<>();
/**
* 针对POST存在,params这种加进来的参数最终拼接之后保存到这里
*/
private String body;
/**
* 资源类型
*/
private String contentType = null;
/**
* 连接超时时间
*/
private int connectionTimeout = SmartHttpClient.CONNECT_TIMEOUT;
/**
* 读数据超时时间
*/
private int readTimeout = SmartHttpClient.READ_TIMEOUT;
/**
* 请求体编码
*/
private String bodyCharset = SmartHttpClient.DEFAULT_CHARSET;
/**
* 返回体编码
*/
private String resultCharset = SmartHttpClient.DEFAULT_CHARSET;
/**
* 返回结果中是否包含headers
*/
private boolean includeHeaders = false;
/**
* 2018-06-18为了文件上传增加的
*/
private List<FormFile> formFiles = new ArrayList<>();
/**
* 为文件下载确定信息
*/
private File file = null;
private Request(String url){this.url = url;}
/**
* 静态方法创建请求
* @param url URL
* @return Request
*/
public static Request of(String url){
return new Request(url);
}
/**************************变种的Setter*******************************/
public Request setUrl(String url) {
this.url = url;
return this;
}
public Request setParams(ArrayListMultimap<String, String> params) {
this.params = params;
return this;
}
public Request setParams(Map<String, String> params) {
params.forEach((k,v)->this.params.put(k,v));
return this;
}
public Request addParam(String key,String value){
this.params.put(key, value);
return this;
}
public Request setHeaders(ArrayListMultimap<String, String> headers) {
this.headers = headers;
return this;
}
public Request setHeaders(Map<String, String> headers) {
headers.forEach((k,v)->this.headers.put(k,v));
return this;
}
public Request addHeader(String key, String value){
this.headers.put(key, value);
return this;
}
public Request setBody(String body) {
this.body = body;
return this;
}
public Request setContentType(String contentType) {
this.contentType = contentType;
return this;
}
public Request setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
return this;
}
public Request setReadTimeout(int readTimeout) {
this.readTimeout = readTimeout;
return this;
}
/****************************Getter**************************/
public String getUrl() {
return url;
}
public ArrayListMultimap<String, String> getParams() {
return params;
}
public ArrayListMultimap<String, String> getHeaders() {
return headers;
}
public String getBody() {
//如果没有Body就将params的参数拼接
if(StrUtil.isBlank(body)){
return HttpUtil.contactMap(params);
}
return body;
}
public String getContentType() {
return contentType;
}
public int getConnectionTimeout() {
return connectionTimeout;
}
public int getReadTimeout() {
return readTimeout;
}
public String getBodyCharset() {
return bodyCharset;
}
public Request setBodyCharset(String bodyCharset) {
this.bodyCharset = bodyCharset;
return this;
}
public String getResultCharset() {
return resultCharset;
}
public Request setResultCharset(String resultCharset) {
this.resultCharset = resultCharset;
return this;
}
public boolean isIncludeHeaders() {
return includeHeaders;
}
public Request setIncludeHeaders(boolean includeHeaders) {
this.includeHeaders = includeHeaders;
return this;
}
public FormFile[] getFormFiles() {
return this.formFiles.toArray(new FormFile[this.formFiles.size()]);
}
public Request addFormFile(FormFile... formFiles) {
if(null != formFiles){
this.formFiles.addAll(Arrays.asList(formFiles));
}
return this;
}
public File getFile() {
return file;
}
public Request setFile(File file) {
this.file = file;
return this;
}
public Request setFile(String filePath) {
this.file = new File(filePath);
return this;
}
}
package cn.zytx.common.http.smart;
import cn.zytx.common.http.HttpException;
import cn.zytx.common.utils.IoUtil;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author xiongshiyan at 2017/12/9
*/
public class Response {
/**
* 返回体
*/
private String body;
/**
* 返回体编码
*/
private String resultCharset = SmartHttpClient.DEFAULT_CHARSET;
/**
* 返回的header
*/
private Map<String,List<String>> headers = new HashMap<>();
private Response(String body) {
this.body = body;
}
public static Response with(String body){
return new Response(body);
}
public static Response with(String body , String resultCharset , Map<String , List<String>> headers){
return new Response(body).setResultCharset(resultCharset).setHeaders(headers);
}
public static Response with(InputStream body , String resultCharset , Map<String , List<String>> headers){
String read = null;
try {
read = IoUtil.read(body, resultCharset);
} catch (IOException e) {
throw new HttpException(e);
}
return new Response(read).setResultCharset(resultCharset).setHeaders(headers);
}
public String getBody() {
return body;
}
public Response setBody(String body) {
this.body = body;
return this;
}
public Map<String, List<String>> getHeaders() {
return headers;
}
public List<String> getHeader(String key) {
return headers.get(key);
}
public String getOneHeader(String key) {
List<String> list = headers.get(key);
if(null == list){return null;}
return list.get(0);
}
public Response addHeader(String key,String value){
List<String> list = headers.get(key);
if(list == null){
list = new ArrayList<>();
if(list.add(value)){
headers.put(key, list);
return this;
} else{
throw new AssertionError("New list violated the list spec");
}
} else {
list.add(value);
return this;
}
}
public Response setHeaders(Map<String, List<String>> headers) {
this.headers = headers;
return this;
}
public String getResultCharset() {
return resultCharset;
}
public Response setResultCharset(String resultCharset) {
this.resultCharset = resultCharset;
return this;
}
@Override
public String toString() {
return body;
}
}
ArrayListMultiMap代表一个key有多个值的map结构,在表达k=v1&k=v2和一个key对应多个header很有用。感谢L.cm。
package cn.zytx.common.utils;
import java.util.*;
/**
* 自己实现的ArrayListMultimap 重复key的map.
* 目前使用的地方 :
* 1.使用监听的type,取出所有的监听器
* 2.Http请求封装参数和header
* @author L.cm email: [email protected] site:http://www.dreamlu.net date 2015年6月25日下午8:36:17
*/
public class ArrayListMultimap<K, V> {
private transient final Map<K, List<V>> map;
public ArrayListMultimap(){
map = new HashMap<>();
}
public ArrayListMultimap(int capacity){
map = new HashMap<>(capacity);
}
private static <V1> List<V1> createList(){
return new ArrayList<>();
}
private static <V1> List<V1> createList(int capacity){
return new ArrayList<>(capacity);
}
/**
* put to ArrayListMultimap
* @param key 键
* @param value 值
* @return boolean
*/
public boolean put(K key, V value){
List<V> list = map.get(key);
if(list == null){
list = createList();
if(list.add(value)){
map.put(key, list);
return true;
} else{
throw new AssertionError("New list violated the list spec");
}
} else if(list.add(value)){
return true;
} else{
return false;
}
}
public Map<K, List<V>> getMap() {
return map;
}
/**
* get List by key
* @param key 键
* @return List
*/
public List<V> get(K key){
List<V> list = map.get(key);
if(list == null){
list = createList();
}
return list;
}
/**
* 获取第一个,对于只有一个值的来说
*/
public V getFirst(K key){
List<V> list = map.get(key);
return (null == list || list.size() == 0) ? null : list.get(0);
}
public Set<K> keySet(){
return map.keySet();
}
/**
* clear ArrayListMultimap
*/
public void clear(){
map.clear();
}
/**
* 从普通map转换而来
*/
public static <K1,V1> ArrayListMultimap<K1,V1> fromMap(Map<K1,V1> map){
if(null == map){
return null;
}
ArrayListMultimap<K1, V1> mapList = new ArrayListMultimap<>(map.size());
map.forEach(mapList::put);
return mapList;
}
}
在此基础上再定义将结果转换为Bean的接口
package cn.zytx.common.http.withconverter;
import cn.zytx.common.http.HttpUtil;
import cn.zytx.common.http.basic.HttpClient;
import cn.zytx.common.http.smart.Request;
import cn.zytx.common.http.smart.Response;
import cn.zytx.common.http.smart.SmartHttpClient;
import java.io.IOException;
import java.util.Map;
/**
* @author xiongshiyan at 2018/7/16
* 返回的结果值直接封装为bean
*/
public interface ConverterSmartHttpClient extends SmartHttpClient , Converter{
ConverterSmartHttpClient setConverter(Converter converter);
default <T> T get(String url, Map<String, String> params, Map<String, String> headers, int connectTimeout, int readTimeout, String resultCharset , Class<T> clazz) throws IOException{
String result = get(url, params, headers, connectTimeout, readTimeout, resultCharset);
return convert(result , clazz);
}
default <T> T get(String url, Map<String, String> params,Map<String,String> headers,int connectTimeout,int readTimeout , Class<T> clazz) throws IOException{
return get(url, params, headers, connectTimeout, readTimeout , DEFAULT_CHARSET , clazz);
}
default <T> T get(String url, Map<String, String> params,Map<String,String> headers , String resultCharset, Class<T> clazz) throws IOException{
return get(url,params,headers,CONNECT_TIMEOUT,READ_TIMEOUT,resultCharset , clazz);
}
default <T> T get(String url, Map<String, String> params,Map<String,String> headers, Class<T> clazz) throws IOException{
return get(url,params,headers,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
default <T> T get(String url, Map<String, String> params,int connectTimeout,int readTimeout , String resultCharset, Class<T> clazz) throws IOException{
return get(url,params,null,connectTimeout,readTimeout,resultCharset , clazz);
}
default <T> T get(String url, Map<String, String> params,int connectTimeout,int readTimeout, Class<T> clazz) throws IOException{
return get(url,params,null,connectTimeout,readTimeout , clazz);
}
default <T> T get(String url,Map<String, String> params , String resultCharset, Class<T> clazz) throws IOException{
return get(url,params,null,CONNECT_TIMEOUT,READ_TIMEOUT,resultCharset , clazz);
}
default <T> T get(String url,Map<String, String> params, Class<T> clazz) throws IOException{
return get(url,params,null,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
default <T> T get(String url , String resultCharset, Class<T> clazz) throws IOException{
return get(url,null,null,CONNECT_TIMEOUT,READ_TIMEOUT , resultCharset , clazz);
}
default <T> T get(String url, Class<T> clazz) throws IOException{
return get(url,null,null,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
default <T> T post(String url, String body, String contentType, Map<String,String> headers, int connectTimeout, int readTimeout, String bodyCharset , String resultCharset , Class<T> clazz) throws IOException{
String result = post(url, body, contentType, headers, connectTimeout, readTimeout, bodyCharset, resultCharset);
return convert(result , clazz);
}
default <T> T post(String url, String body, String contentType,Map<String,String> headers,int connectTimeout,int readTimeout, Class<T> clazz) throws IOException{
return post(url,body,contentType,headers,connectTimeout,readTimeout, DEFAULT_CHARSET,DEFAULT_CHARSET , clazz);
}
default <T> T post(String url, String body, String contentType,Map<String,String> headers, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url,body,contentType,headers,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset , clazz);
}
default <T> T post(String url, String body, String contentType,Map<String,String> headers, Class<T> clazz) throws IOException{
return post(url,body,contentType,headers,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
default <T> T post(String url, String body, String contentType,int connectTimeout,int readTimeout, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url,body,contentType,null,connectTimeout,readTimeout,bodyCharset,resultCharset , clazz);
}
default <T> T post(String url, String body, String contentType,int connectTimeout,int readTimeout, Class<T> clazz) throws IOException{
return post(url,body,contentType,null,connectTimeout,readTimeout , clazz);
}
/**
* @see HttpClient#post(String, String, String, Map, int, int, String, String)
*/
default <T> T post(String url, String body, String contentType, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url,body,contentType,null,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset , clazz);
}
default <T> T post(String url, String body, String contentType, Class<T> clazz) throws IOException{
return post(url,body,contentType,null,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
default <T> T postJson(String url, String body, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url,body,JSON_WITH_DEFAULT_CHARSET,null,CONNECT_TIMEOUT,READ_TIMEOUT,bodyCharset,resultCharset , clazz);
}
default <T> T postJson(String url, String body, Class<T> clazz) throws IOException{
return post(url,body,JSON_WITH_DEFAULT_CHARSET,null,CONNECT_TIMEOUT,READ_TIMEOUT , clazz);
}
/**参数用 =和& 连接*/
default <T> T post(String url, Map<String, String> params,Map<String,String> headers, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url, HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,headers,bodyCharset,resultCharset , clazz);
}
default <T> T post(String url, Map<String, String> params,Map<String,String> headers, Class<T> clazz) throws IOException{
return post(url, HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,headers , clazz);
}
default <T> T post(String url, Map<String, String> params, String bodyCharset , String resultCharset, Class<T> clazz) throws IOException{
return post(url,HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,null,bodyCharset,resultCharset , clazz);
}
default <T> T post(String url, Map<String, String> params, Class<T> clazz) throws IOException{
return post(url,HttpUtil.contactMap(params),FORM_URLENCODED_WITH_DEFAULT_CHARSET,null , clazz);
}
default <T> T get(Request request , Class<T> clazz) throws IOException{
Response response = get(request);
return convert(response.getBody() , clazz);
}
default <T> T post(Request request , Class<T> clazz) throws IOException{
Response response = post(request);
return convert(response.getBody() , clazz);
}
}
该接口还实现了Converter接口,于是此接口中的所有方法就可以直接调用Converter接口的convert方法,Converter接口用于将字符串转换为Bean。
package cn.zytx.common.http.withconverter;
/**
* @author xiongshiyan at 2018/7/16
*/
public interface Converter {
/**
* 将字符串[可能是Json、Xml或者其他格式]转为Bean
* @param src 原字符串
* @param clazz BeanClass
* @param <R> Bean
* @return R
*/
<R> R convert(String src , Class<R> clazz);
}