# http-fetch **Repository Path**: niuss/http-fetch ## Basic Information - **Project Name**: http-fetch - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2018-11-27 - **Last Updated**: 2020-12-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README

让http请求的调用更优雅

## 概述 当我们提到java调用http请求时,我们想到的是HttpClient或是内置的HttpUrlConnention。 然后会写下如下一串的代码访问http接口: ``` bash HttpClient client = new HttpClient(); client.getHostConfiguration().setProxy("127.0.0.1", 8888); client.getHostConfiguration().setHost("bl.ocks.org", 80, "http"); GetMethod getMethod = new GetMethod("/mbostock/raw/4090846/us-congress-113.json"); client.executeMethod(getMethod); //打印服务器返回的状态 System.out.println(getMethod.getStatusLine().getStatusCode()); if(getMethod.getStatusLine().getStatusCode() == 200){ //打印结果页面 String response = new String(getMethod.getResponseBodyAsString().getBytes("8859_1")); //打印返回的信息 System.out.println(response); } getMethod.releaseConnection(); ``` 可是我们是不是有一种更优雅的方式呢?类似于MyBatis,通过一定的配置,然后在需要请求http接口的时候只需要调用一个接口函数便可以完成上述代码的工作。 这就是HttpFetch的初衷,让http请求的调用更优雅。 ## 下载 ``` bash git clone https://github.com/youzan/httpfetch.git ``` ## QuickStart https://github.com/youzan/httpfetch/wiki/QuickStart ## 对象 * ParameterResolver:api参数解析类,自带的可以对数组、bean、简单类型等参数进行解析并封装成Get、Post、Form等类型请求的参数。也可以通过Url注解灵活定义api接口的请求地址。 * Convertor:返回数据封装类,自带的仅支持简单类型和JSON类型的数据进行封装。通过扩展可以实现更多的转换方式。 * Chain: 责任链模式,一层层对请求进行加工和处理。里面比较重要的是ParameterResolverChain、GenerateResponseChain和ExecuteRequestChain。ParameterResolverChain负责对参数进行处理,GenerateResponseChain负责对返回结果进行处理,ExecuteRequestChain负责最后的请求发送。 * ResourceReader: 配置信息读取类,负责对各组件单元的读取并最终传给HttpApiConfiguration类。 * HttpApiConfiguration: 负责对ResourceReader读取后的配置信息进行封装,然后将配置信息传给HttpApiService类。 * HttpApiService: 负责最后的代理类生成和缓存; ## 框架 * 初始化过程 初始化过程可以选择spring和xml两种。spring的方式直接将生成的代理类注册到BeanDefinitionRegistry(可见HttpApiClassPathBeanDefinitionScanner源码),xml方式可以在没有spring组件的情况下独立运行(见单测MbostockApiUseXmlTest)。两种方式都可以完成Chain、ParamterResolver和Convertor注册。 ![初始化过程](http://onlz2qizd.bkt.clouddn.com/http-fetch2.jpeg) * 请求处理流程 请求者发起请求时,会通过配置的各个Chain单元,一步一步的处理和封装参数并发送最终的Http请求,最后将返回的值进行封装。 ![请求处理流程](http://onlz2qizd.bkt.clouddn.com/http-fetch.jpeg?a=4#1) ## 使用 ### Maven ``` xml com.github.youzan http-fetch    1.1.6 ``` ### 非spring调用 1.创建http-api.xml配置文件: ``` xml ``` 2.编写MbostockApi接口类: ``` java package com.github.nezha.httpfetch.mbostock.api; import com.github.nezha.httpfetch.HttpApi; import com.github.nezha.httpfetch.Header; import com.github.nezha.httpfetch.mbostock.vo.UsCongressResponseVo; /** * Created by daiqiang on 17/3/14. */ public interface MbostockApi { @HttpApi(timeout = 1000, headers = {@Header(key="user-agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")}) UsCongressResponseVo getUsCongress(); } ``` 3.编写测试类: ``` java SourceReader xmlReader = new XmlReader(Arrays.asList("httpapi.xml")); HttpApiConfiguration configuration = new HttpApiConfiguration(); configuration.setSourceReaders(Arrays.asList(xmlReader)); configuration.init(); HttpApiService service = new HttpApiService(configuration); service.init(); MbostockApi mbostockApi = service.getOrCreateService(MbostockApi.class); UsCongressResponseVo responseVo = mbostockApi.getUsCongress(); System.out.println("type=="+responseVo.getType()); System.out.println("arcs->size=="+responseVo.getArcs().size()); System.out.println("objects->districts->bbox->size=="+responseVo.getObjects().getDistricts().getBbox().size()); System.out.println("objects->districts->type=="+responseVo.getObjects().getDistricts().getType()); System.out.println("objects->districts->geometries->size=="+responseVo.getObjects().getDistricts().getGeometries().size()); System.out.println("transform->scale=="+responseVo.getTransform().getScale()); System.out.println("transform->translate=="+responseVo.getTransform().getTranslate()); ``` 以上就是非spring方式的调用 ### spring方式的调用 1.创建application-httpapi.xml文件: ``` xml ``` 2.编写MbostockApi接口类: ``` java package com.github.nezha.httpfetch.mbostock.api; import com.github.nezha.httpfetch.HttpApi; import com.github.nezha.httpfetch.Header; import com.github.nezha.httpfetch.mbostock.vo.UsCongressResponseVo; /** * Created by daiqiang on 17/3/14. */ public interface MbostockApi { @HttpApi(timeout = 1000, headers = {@Header(key="user-agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")}) UsCongressResponseVo getUsCongress(); } ``` 3.编写测试类: ``` java public class MbostockApiTest extends BaseTest { @Autowired private MbostockApi mbostockApi; @Test public void test(){ UsCongressResponseVo responseVo = mbostockApi.getUsCongress(); System.out.println("type=="+responseVo.getType()); System.out.println("arcs->size=="+responseVo.getArcs().size()); System.out.println("objects->districts->bbox->size=="+responseVo.getObjects().getDistricts().getBbox().size()); System.out.println("objects->districts->type=="+responseVo.getObjects().getDistricts().getType()); System.out.println("objects->districts->geometries->size=="+responseVo.getObjects().getDistricts().getGeometries().size()); System.out.println("transform->scale=="+responseVo.getTransform().getScale()); System.out.println("transform->translate=="+responseVo.getTransform().getTranslate()); } } ``` ### URL映射 url的映射使用了三种方式: 1.使用xml进行配置: ``` xml ``` 2.使用注解方式: ``` java package com.github.nezha.httpfetch.bookworm.api; import com.github.nezha.httpfetch.Header; import com.github.nezha.httpfetch.HttpApi; import com.github.nezha.httpfetch.resolver.RequestBody; import java.util.Map; /** * Created by daiqiang on 17/6/16. */ public interface AlarmJobApi { @HttpApi(method = "POST", headers = @Header(url = "http://alert.s.qima-inc.com/api/v1/alert", key = "Content-type", value = "application/json"), timeout = 2000) String alert(@RequestBody Map param); } ``` 3.使用参数方式传入: ``` java @HttpApi(timeout = 1000, headers = {@Header(key="user-agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")}) UsCongressResponseVo getUsCongress(@URL String url); ``` 测试类: ``` java public void test_url_param(){ String url = "https://bl.ocks.org/mbostock/raw/4090846/us-congress-113.json"; UsCongressResponseVo responseVo = mbostockApi.getUsCongress(url); System.out.println("type=="+responseVo.getType()); System.out.println("arcs->size=="+responseVo.getArcs().size()); System.out.println("objects->districts->bbox->size=="+responseVo.getObjects().getDistricts().getBbox().size()); System.out.println("objects->districts->type=="+responseVo.getObjects().getDistricts().getType()); System.out.println("objects->districts->geometries->size=="+responseVo.getObjects().getDistricts().getGeometries().size()); System.out.println("transform->scale=="+responseVo.getTransform().getScale()); System.out.println("transform->translate=="+responseVo.getTransform().getTranslate()); } ``` ### 参数封装 1.Get请求参数: 使用QueryParam注解标记,并填写参数的名称 ``` java @HttpApi(timeout = 2000, url = "http://bookworm365.com/uploadImage") @BookWormApi UploadFileResponseVo uploadFile(@QueryParam("name") String name, @QueryParam("n_value") String nValue); ``` 2.Post请求参数: 使用PostParam注解标记,并填写参数的名称 ``` java Map audit(@PostParam("advertisementId") Integer advertisementId); ``` 3.Form请求参数: 使用FormParam注解标记,并填写参数的名称 ``` java @HttpApi(timeout = 2000, url = "http://bookworm365.com/uploadImage") @BookWormApi UploadFileResponseVo uploadFile(@FormParam("file") File file, @QueryParam("name") String name, @QueryParam("n_value") String nValue); ``` 4.BeanParam注解使用: 当我们传递一个bean做为参数,但是希望对这个bean进行解析然后作为http请求参数时,我们可以使用BeanParam注解。 ``` java @HttpApi(timeout = 2000, url = "http://bookworm365.com/uploadImage") @BookWormApi UploadFileResponseVo uploadFile(@BeanParam @QueryParam UploadFileRequestVo requestVo); ``` ``` java package com.github.nezha.httpfetch.bookworm.vo; import com.alibaba.fastjson.annotation.JSONField; import java.io.File; public class UploadFileRequestVo { @JSONField(name = "file") private File file; private String name; @JSONField(name="n_value") private String nValue; public File getFile() {return file;} public void setFile(File file) {this.file = file;} public String getName() {return name;} public void setName(String name) {this.name = name;} public String getnValue() {return nValue;} public void setnValue(String nValue) {this.nValue = nValue;} } ``` http的请求最终为:http://bookworm365.com/uploadImage?file=XXX&name=XXX&n_value=XXX 5.RequestBody注解使用: 当你需要传递消息体给服务器是,可以通过该注解。 例如我们想要传递一个application\json的请求: ``` java @HttpApi(method = "POST",timeout = 2000,headers = {@Header(key = "Content-type", value = "application/json;charset=UTF-8")}) @WechatApi WechatBaseResponseVo add(@RequestBody AddCustomAudiencesRequestVo requestVo); ``` ### 结果封装 结果封装默认支持简单类型和JSON两种: 1.简单类型,如果返回值是String、int、long等,api的返回对象可以直接指定对应类: ``` java @HttpApi(timeout = 2000, url = "http://bookworm365.com/checkHeader") @BookWormApi String checkHeader(); ``` 2.JSON,如果返回值是一个json字符串,可以直接编写对应的bean作为返回类,内部使用fastjson进行反序列化: ``` java @HttpApi(timeout = 1000, headers = {@Header(key="user-agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")}) UsCongressResponseVo getUsCongress(); ``` ``` java package com.github.nezha.httpfetch.mbostock.vo; import com.alibaba.fastjson.annotation.JSONField; import java.util.List; public class UsCongressResponseVo { @JSONField(name="type") private String type; @JSONField(name="objects") private ObjectsVo objects; @JSONField(name="arcs") private List>> arcs; @JSONField(name="transform") private TransformVo transform; public String getType() {return type;} public void setType(String type) {this.type = type;} public ObjectsVo getObjects() {return objects;} public void setObjects(ObjectsVo objects) {this.objects = objects;} public List>> getArcs() {return arcs;} public void setArcs(List>> arcs) {this.arcs = arcs;} public TransformVo getTransform() {return transform;} public void setTransform(TransformVo transform) {this.transform = transform;} } ``` ``` java package com.github.nezha.httpfetch.mbostock.vo; import com.alibaba.fastjson.annotation.JSONField; import com.github.nezha.httpfetch.BaseTest; public class ObjectsVo { @JSONField(name="districts") private DistrictsVo districts; public DistrictsVo getDistricts() {return districts;} public void setDistricts(DistrictsVo districts) {this.districts = districts;} } ``` 另外返回的类还支持泛型。 ``` java @HttpApi(timeout = 1000, headers = {@Header(key="user-agent", value = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36")}) UsCongressResponseVo getUsCongress(); ``` ### 重试策略 需要升级到1.2.0之后才可以使用。
HttpApi注解中增加了retry和retryPolicy两个变量:
retry:重试次数;
retryPolicy:重试策略,默认为ConnectFailureRetryPolicy,超时和连接异常会进行重试;
#### 自定义重试策略 类图: ![类图](http://img.blog.csdn.net/20180223235850203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2hqMTk4NzA0MDY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70) 所有的重试策略需要继承RetryPolicy接口,并实现needRetry函数。 ``` java /** * 重试校验接口 */ public interface RetryPolicy { /** * * @param result http请求结果 * @param retryTimes 重试次数 * @param remainRetryTimes 剩余重试次数 * @return */ boolean needRetry(HttpResult result, int retryTimes, int remainRetryTimes); } ``` ConnectFailureRetryPolicy: ``` java public class ConnectFailureRetryPolicy implements RetryPolicy {   private static final Logger LOGGER = LoggerFactory.getLogger(ConnectFailureRetryPolicy.class); /** * 如果是网络异常则重试 * @param result http请求结果 * @param retryTimes 重试次数 * @param remainRetryTimes 剩余重试次数 * @return */ @Override public boolean needRetry(HttpResult result, int retryTimes, int remainRetryTimes) { Exception e = result.getException(); if(e instanceof SocketTimeoutException || e instanceof ConnectException){ LOGGER.info("超时重试: {}, 重试次数: {} 剩余次数: {}", e, retryTimes, remainRetryTimes); return true; } return false; } }   ``` 实现完自己的重试策略后,只需要在HttpApi注解中设置retryPolicy的值就可以了。 更多示例可以在项目的`test`目录中查看 ## 开源协议 本项目基于 [MIT](https://zh.wikipedia.org/wiki/MIT%E8%A8%B1%E5%8F%AF%E8%AD%89)协议,请自由地享受和参与开源。 ## 贡献 如果你有好的意见或建议,欢迎给我们提 [issue] 或 [PR],为优化 [http-fetch] 贡献力量