Сделать вызов http-запросов более элегантным
Обзор
Когда мы говорим о HTTP-запросах, вызываемых java, мы думаем о HttpClient или встроенном HttpUrlConnention. Затем напишите следующий код для доступа к http-интерфейсу:
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-запросов более элегантным.
скачать
git clone https://github.com/youzan/httpfetch.git
QuickStart
https://github.com/youzan/httpfetch/wiki/QuickStart
объект
- ParameterResolver: класс анализа параметров API, который может анализировать массивы, bean-компоненты, простые типы и другие параметры и инкапсулировать их в параметры запросов Get, Post, Form и других типов. Вы также можете гибко определить адрес запроса API-интерфейса через аннотацию Url.
- Преобразователь: возвращает класс инкапсуляции данных, который поддерживает только простые типы и типы данных JSON для инкапсуляции. Дополнительные преобразования могут быть достигнуты путем расширения.
- Цепочка: Модель цепочки ответственности, обработка и обработка запросов слой за слоем. Более важными из них являются ParameterResolverChain, GenerateResponseChain и ExecuteRequestChain. ParameterResolverChain отвечает за обработку параметров, GenerateResponseChain — за обработку возвращаемых результатов, а ExecuteRequestChain — за отправку окончательного запроса.
- ResourceReader: класс чтения информации о конфигурации, отвечающий за чтение каждой единицы компонента и, наконец, передачу ее в класс HttpApiConfiguration.
- HttpApiConfiguration: отвечает за инкапсуляцию информации о конфигурации, считанной ResourceReader, и затем передачу информации о конфигурации в класс HttpApiService.
- HttpApiService: отвечает за генерацию и кэширование окончательного прокси-класса;
Рамка
-
процесс инициализации
Процесс инициализации может выбирать между spring и xml. Метод Spring напрямую регистрирует созданный прокси-класс в BeanDefinitionRegistry (см. исходный код HttpApiClassPathBeanDefinitionScanner), а метод xml может выполняться независимо без компонента Spring (см. одиночный тест MbostockApiUseXmlTest). Регистрация цепочки, ParamterResolver и Converter может быть выполнена обоими способами.
-
Процесс обработки запроса Когда запрашивающая сторона инициирует запрос, по соответствующим блокам организована цепочка, шаг за шагом передаются параметры процесса и окончательный пакет Http-запроса, и, наконец, инкапсулированные возвращаемые значения.
использовать
Maven
<dependency>
<groupId>com.github.youzan</groupId>
<artifactId>http-fetch</artifactId>
<version>1.1.6</version>
</dependency>
не весенний вызов
1. Создайте файл конфигурации http-api.xml:
<?xml version="1.0" encoding="UTF-8"?>
<setting>
<!-- 请求处理链 -->
<chains>
</chains>
<!-- 参数处理类 -->
<argumentResolvers>
</argumentResolvers>
<!-- 结果处理类 -->
<resultConvertors>
</resultConvertors>
<!-- api和url映射关系 -->
<aliases>
<alias key="mbostockApi.getUsCongress" value="https://bl.ocks.org/mbostock/raw/4090846/us-congress-113.json" />
</aliases>
</setting>
2. Напишите класс интерфейса MbostockApi:
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. Напишите тестовый класс:
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
1. Создайте файл application-httpapi.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
">
<bean id="springReader" class="com.github.nezha.httpfetch.spring.SpringReader" >
<!-- 请求处理链 -->
<property name="chains" >
<list>
<bean class="com.github.nezha.httpfetch.bookworm.chains.BookWormTokenChain" />
</list>
</property>
<!-- 参数处理类 -->
<property name="parameterResolvers">
<list>
</list>
</property>
<!-- 结果处理类 -->
<property name="convertors">
<list>
</list>
</property>
<!-- api和url映射关系 -->
<property name="urlAlias">
<map>
<entry key="mbostockApi.getUsCongress" value="${mock.host}/mbostock/raw/4090846/us-congress-113.json" />
</map>
</property>
</bean>
<bean id="httpApiConfiguration" class="com.github.nezha.httpfetch.HttpApiConfiguration" init-method="init">
<property name="sourceReaders">
<list>
<ref bean="springReader" />
</list>
</property>
</bean>
<bean id="httpApiService" class="com.github.nezha.httpfetch.HttpApiService" init-method="init">
<constructor-arg index="0" ref="httpApiConfiguration" />
</bean>
<!-- http api代理注册 -->
<bean class="com.github.nezha.httpfetch.spring.HttpApiScannerConfigurer">
<property name="basePackage" value="com.github.nezha.httpfetch.bookworm.api,com.github.nezha.httpfetch.mbostock.api,com.github.nezha.httpfetch.youzan.api" />
</bean>
</beans>
2. Напишите класс интерфейса MbostockApi:
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. Напишите тестовый класс:
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 для настройки:
<aliases>
<alias key="mbostockApi.getUsCongress" value="https://bl.ocks.org/mbostock/raw/4090846/us-congress-113.json" />
</aliases>
2. Используйте метод аннотации:
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<String, Object> param);
}
3. Используйте метод параметра для передачи:
@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);
Тестовый класс:
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.Получить параметры запроса: Используйте тег аннотации QueryParam и введите имя параметра
@HttpApi(timeout = 2000, url = "http://bookworm365.com/uploadImage")
@BookWormApi
UploadFileResponseVo uploadFile(@QueryParam("name") String name,
@QueryParam("n_value") String nValue);
2.Параметры почтового запроса: Используйте тег аннотации PostParam и заполните имя параметра
Map audit(@PostParam("advertisementId") Integer advertisementId);
3.Параметры запроса формы: Используйте тег аннотации FormParam и заполните имя параметра
@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 используются: Мы можем использовать аннотацию BeanParam, когда передаем bean-компонент в качестве параметра, но хотим проанализировать bean-компонент как параметр HTTP-запроса.
@HttpApi(timeout = 2000, url = "http://bookworm365.com/uploadImage")
@BookWormApi
UploadFileResponseVo uploadFile(@BeanParam @QueryParam UploadFileRequestVo requestVo);
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:
@HttpApi(method = "POST",timeout = 2000,headers = {@Header(key = "Content-type", value = "application/json;charset=UTF-8")})
@WechatApi
WechatBaseResponseVo<AddCustomAudiencesResponseVo> add(@RequestBody AddCustomAudiencesRequestVo requestVo);
инкапсуляция результата
Инкапсуляция результата по умолчанию поддерживает как простые типы, так и JSON: 1. Простой тип, если возвращаемое значение String, int, long и т. д., возвращаемый объект API может напрямую указывать соответствующий класс:
@HttpApi(timeout = 2000, url = "http://bookworm365.com/checkHeader")
@BookWormApi
String checkHeader();
2.JSON, если возвращаемое значение представляет собой строку json, вы можете напрямую написать соответствующий bean-компонент в качестве возвращаемого класса и использовать fastjson внутри для десериализации:
@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();
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<List<List<Integer>>> 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<List<List<Integer>>> getArcs() {return arcs;}
public void setArcs(List<List<List<Integer>>> arcs) {this.arcs = arcs;}
public TransformVo getTransform() {return transform;}
public void setTransform(TransformVo transform) {this.transform = transform;}
}
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;}
}
Кроме того, возвращаемый класс также поддерживает дженерики.
@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<TransformVo> getUsCongress();
стратегия повторных попыток
Его необходимо обновить до 1.2.0, прежде чем его можно будет использовать.
В аннотацию HttpApi добавляются две переменные, retry и retryPolicy:
retry: количество попыток;
retryPolicy: политика повтора, значение по умолчанию ConnectFailureRetryPolicy, время ожидания и исключение соединения будут повторены;
Пользовательская стратегия повторной попытки
Диаграмма класса:
Все политики повторных попыток должны наследовать интерфейс RetryPolicy и реализовывать функцию needRetry.
/**
* 重试校验接口
*/
public interface RetryPolicy {
/**
*
* @param result http请求结果
* @param retryTimes 重试次数
* @param remainRetryTimes 剩余重试次数
* @return
*/
boolean needRetry(HttpResult result, int retryTimes, int remainRetryTimes);
}
Коннектфаилуреретриполици:
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;
}
}
После реализации собственной политики повторных попыток вам нужно только установить значение retryPolicy в аннотации HttpApi.
Больше примеров можно найти в проектеtest
Посмотреть в каталоге
протокол с открытым исходным кодом
Этот проект основан наMITСоглашение, пожалуйста, наслаждайтесь и участвуйте в открытом исходном коде свободно.
способствовать
Если у вас есть хорошие комментарии или предложения, пришлите нам [issue] или [PR], чтобы внести свой вклад в оптимизацию [http-fetch].