同步操作将从 AlexGao/WSHttpHelper 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
HttpHelper是一个轻量级Http请求框架,致力于为Http请求提供简洁明了的接口,使开发人员更专注于业务,提高效率。v2.0版本提供了一套Http请求处理流程和规范,能够在请求过程的Pipeline中添加丰富的功能处理。
允许开发人员针对REST接口定义客户端对应的请求Interface
类,只需要根据接口定义作出对应配置即可实现类似于RPC
的声明式调用,可使请求接口简洁,规范,高效,更有可阅读性。
对于请求HTML
资源,可以使用WebClient
不仅支持JS渲染执行Ajax
请求,还提供了一套数据抽取功能,支持xpath,css,regex任意配置到数据字段的规则抽取,可将文本数据抽取为复杂的(嵌套的)格式化的对象数据。
v2.0不仅提供了更加灵活的扩展机制、更多的内置功能、更快的效率;更采用了全新的设计思想。
设计考虑代码的复用,模块的复用,更考虑了模式的复用;请求的Pipeline是针对模版定义,不是针对单一接口,这种约定可以在准备开发新的处理器时需要考虑从更高层的模版共性出发,作出更通用和公用的处理器。
同时模版配置TempleConfig
支持继承关系,可以先定义通用的顶级父Temple配置,使用继承和泛化思想实现个性化配置。
使用规范和约定将针对接口请求的问题转换成根据业务接口的Client定义,仅需Copy接口定义输入输出POJO即可完成一个接口请求的定义。
当前Pipeline中内置的Handler能够满足绝大部分的在JAVA中使用http请求的场景。
PS:最近花了点时间重构了HttpHelper,可以适配SpringBoot,同时使用全新思路解决方案。(文档逐步完善中)
另外请关注我的另外一个开源项目(推荐项目)《一个分布式任务分发框架核心包》https://gitee.com/wolfsmoke/ws-task
RequestHandler
)和请求后处理(实现ResponseHandler
)
@HandlerOrder
和添加的顺序共同决定;
RequestHandler
:从head向后处理,level=SYSTEM优先,value越小越优先,添加顺序靠前优先;ResponseHandler
:从tail向前处理,level=SYSTEM优先,value越大越优先,添加顺序靠后优先;http-helper-spring
使用@EnableHttpHelper
开启功能@HttpOperation
注解,即可通过Proxy动态创建实例,适用于接口访问,可实现类似于@Feign的伪RPC声明式调用。具体说明见后续详细说明。http-helper
属性可以通过spring-boot-starter自动生成对象。application/json
的数据,通过在Pipeline添加ParseJsonHandler
即可将数据解析为对象或Map。application/xml
的数据,通过在Pipeline添加ParseXmlHandler
即可将数据解析为对象或Map。(中间使用了JSON作为转换)text/html
的数据,通过在Pipeline添加ParseXmlHandler
和配置ParseField
可从HTML中提取数据,并转换为对象或Map。
ParseField
支持String,List,Object类型。其中List的项和Object通过配置childField可以定义为一个复杂对象。ParseField
支持从HTML中抽取数据的表达式支持CSS(类似JQuery),XPATH,REGEX,可以适用于绝大多数数据抽取场景。SaveFileHandler
将数据保存成文件。详情见module:http-helper-sample下说明文档README.MD
示例为SpringBootApplication,使用了
@EnableHttpHelper
开启功能.
Builder方式提供灵活的配置方式创建HttpHelper对象,在SpringBoot项目中可以使用@Bean注解将对象放在容器中,以便注入使用.
示例功能:当前示例是能够根据传入的URL,将URL中全部的图片下载到本地.
创建HttpHelper对象代码片段如下:
/**
* 请求并解析网页中图片
* @return
*/
@Bean
public HttpHelper searchImgHttp(){
RequestTemplate template = RequestTemplateBuilder.builder()
// 客户端配置
.buildClientConfig()
.clientType(ClientType.WEB_CLIENT)
.enableSsl(true)
.enableCookie(true)
.followRedirects(true)
.threadNumber(4)
.timeout(10_000)
.privateClient(false)
.requestConfig()
// 添加一个HTML网页解析字段:类型为List,使用CSS解析,
// 解析表达式为img@src ==> 表示获取网页中全部的图片连接
.addParseHtmlFields(new ParseField("imageList", ParseFieldType.LIST, "img@src", ExpressionType.CSS))
.method(HttpMethod.GET)
.charset("UTF-8")
// 添加:默认请求前处理器
.addHandler(DefaultHandlers.requestHandlers())
// 添加:解析响应内容类型
.addHandler(new ParseResponseTypeHandler())
// 添加:根据ParseHtmlFields解析HTML处理器
.addHandler(new ParseHtmlHandler())
.end()
.build();
// 根据template创建一个HttpHelper对象
return HttpHelperBuilder.builder().builderByTemplate(template);
}
/**
* 保存图片HttpHelper
* @return
* @throws URISyntaxException
*/
@Bean
public HttpHelper saveImgHttp() throws URISyntaxException {
RequestTemplate template = RequestTemplateBuilder.builder()
// 客户端配置:使用默认OkHttpClient配置
.clientConfig(DefaultClientConfig.okHttpClientConfig(4,3_000,true))
.buildRequestConfig()
.method(HttpMethod.GET)
.charset("UTF-8")
// 添加:默认请求前处理器
.addHandler(DefaultHandlers.requestHandlers())
// 添加:解析响应内容类型
.addHandler(new ParseResponseTypeHandler())
// 添加:保存文件Handler
.addHandler(saveFileHandler())
.end()
.build();
return HttpHelperBuilder.builder().builderByTemplate(template);
}
/**
* 保存文件Handler
* @return
* @throws URISyntaxException
*/
@Bean
public ResponseHandler saveFileHandler() throws URISyntaxException {
Path path = Paths.get(this.getClass().getResource("/").toURI());
String savePath = path.toString()+"/download";
SaveFileHandler saveFileHandler = new SaveFileHandler(savePath);
saveFileHandler.setFollowUrlPath(true);
log.info("savePath:{}",savePath);
return saveFileHandler;
}
Annotation可以十分方便的定义一个请求接口,通过动态Proxy生成具体实例,每一个方法会对应一个HttpHelper实例,根据@Get,@Post等注解生成对应的配置.
适用于:将一个http请求定义成声明式接口调用请求.更加适用于针对API定义对应的调用接口.
/**
* 定义一个接口,使用@HttpOperation注解
*/
@HttpClient(
clientType = ClientType.WEB_CLIENT, timeout = 10_000,
enableSsl = true, enableCookie = true, followRedirects = true,
privateClient = true
)
@HttpOperation
public interface TopBaidu {
/**
* 使用@Get(value=rootUrl),指定响应类型
* @return
*/
@Get(
value = "http://top.baidu.com/buzz?b=1&fr=topindex",
responseEntity = TopNews.class,
charset = "GBK"
)
TopNews getTopNews();
/**
* 使用@Get,指定响应类型;通过传入url执行
* @return
*/
@Get(
responseEntity = TopNews.class,
charset = "GBK"
)
TopNews getTopNewsByUrl(String url);
}
// 响应数据
@ResponseEntity
public class TopNews {
// 通过XPATH解析数据为一个List,List的Item是一个复杂对象
@ResponseField(fieldName = "newsItems",fieldType = ParseFieldType.LIST,
parseExpression = "//table[contains(@class,'list-table')]/tbody/tr",expressionType = ExpressionType.XPATH)
private List<NewsItem> newsItems;
}
// List的Item
@ResponseEntity
public class NewsItem {
// 使用相对的XPATH解析数据
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = "./td[contains(@class,'first')]",expressionType = ExpressionType.XPATH)
private String number;
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = ".//a[contains(@class,'list-title')]",expressionType = ExpressionType.XPATH)
private String title;
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = "./td[contains(@class,'tc')]/a[1]/@href",expressionType = ExpressionType.XPATH)
private String newsLink;
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = "./td[contains(@class,'tc')]/a[2]/@href",expressionType = ExpressionType.XPATH)
private String videoLink;
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = "./td[contains(@class,'tc')]/a[3]/@href",expressionType = ExpressionType.XPATH)
private String imageLink;
@ResponseField(fieldType = ParseFieldType.STRING,parseExpression = "./td[contains(@class,'last')]",expressionType = ExpressionType.XPATH)
private String last;
}
根据数据开放平台的接口描述,定义对应的接口,即可实现类似于@Feign的伪RPC声明式调用
接口文档:http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi
/**
* 根据API接口的说明文档定义API请求接口
*/
// 本接口全局Client
@HttpClient(
clientType = ClientType.OK_HTTP_CLIENT,
privateClient = true,
timeout = 3_000, threadNumber = 4,
enableCookie = false,followRedirects = false,enableSsl = false
)
// 本节口全局Request配置
@HttpRequest(
rootUrl = "http://api.map.baidu.com/place/v2",
contentType = ContentType.X_WWW_FORM_URLENCODED,
responseType = ResponseType.JSON,
method = HttpMethod.GET
)
@HttpOperation
public interface BaiduMapSearchApi {
/**
* 使用注解指定请求和响应实体,参数为实体
* @param path 请求路径
* @param param 参数实体类
* @return 响应的实体
*/
@Get(
requestEntity = SearchRegionParam.class,// 请求的参数实体类:属性有参数注解配置
responseEntity = SearchRegionResult.class,// 响应的实体类:可不指定,调用时传入
handlers = {BuildSnHandler.class}// 添加生成sn的url的请求处理器(内置默认处理器会自动全部添加)
)
SearchRegionResult searchRegionByEntity(String path, SearchRegionParam param);
/**
* 使用注解配置请求,参数为Map,响应为泛型
* @param path 请求路径
* @param map Map参数
* @param outputClass 响应的类
* @param <T> 响应的类型:可以为Map或JSON对应的实体类
* @return
*/
@Get(
requestParams = {// 请求的参数注解配置
@RequestParam(fieldName = "query",required = true,example = "天安门、美食",description = "检索关键字"),
@RequestParam(fieldName = "tag",example = "美食",description = "检索分类偏好"),
@RequestParam(fieldName = "region",required = true,example = "北京、131(北京的code)、海淀区、全国,等",description = "检索行政区划区域"),
@RequestParam(fieldName = "city_limit",defaultValue = "true",example = "true、false",description = "区域数据召回限制,为true时,仅召回region对应区域内数据。"),
@RequestParam(fieldName = "extensions_adcode",defaultValue = "true",example = "true、false",description = "是否召回国标行政区划编码,"),
@RequestParam(fieldName = "output",defaultValue = "json",example = "json或xml",description = "输出格式为json或者xml"),
@RequestParam(fieldName = "scope",defaultValue = "1",example = "1、2",description = "检索结果详细程度。取值为1 或空,则返回基本信息;取值为2,返回检索POI详细信息"),
@RequestParam(fieldName = "filter",example = "sort_name:distance|sort_rule:1",description = "检索过滤条件。当scope取值为2时,可以设置filter进行排序。"),
@RequestParam(fieldName = "coord_type",defaultValue = "3",example = "1、2、3(默认)、4",description = "坐标类型,1(wgs84ll即GPS经纬度),2(gcj02ll即国测局经纬度坐标),3(bd09ll即百度经纬度坐标),4(bd09mc即百度米制坐标)"),
@RequestParam(fieldName = "ret_coordtype",example = "gcj02ll",description = "可选参数,添加后POI返回国测局经纬度坐标"),
@RequestParam(fieldName = "page_size",defaultValue = "10",example = "10",description = "单次召回POI数量,默认为10条记录,最大返回20条。多关键字检索时,返回的记录数为关键字个数*page_size。"),
@RequestParam(fieldName = "page_num",defaultValue = "0",example = "0、1、2",description = "分页页码,默认为0,0代表第一页,1代表第二页,以此类推。"),
@RequestParam(fieldName = "ak",required = true,description = "开发者的访问密钥,必填项。v2之前该属性为key。"),
@RequestParam(fieldName = "sn",description = "开发者的权限签名。"),
@RequestParam(fieldName = "timestamp",description = "设置sn后该值必填。")
},
handlers = {BuildSnHandler.class}// 添加生成sn的url的请求处理器(内置默认处理器会自动全部添加)
)
<T> T searchRegionByMap(String path, Map<String,String> map, Class<T> outputClass);
}
# 在application.yml中添加http-helper属性,即可使用@Autowired private HttpHelper httpHelper;注入对象.
http-helper:
client:
client-type: web_client
enable-cookie: true
follow-redirects: true
enable-ssl: true
private-client: true
thread-number: 4
timeout: 3000
request:
charset: utf-8
content-type: empty
default-headers:
- name: Accept
value: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
method: get
default-handler: true
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。