Проект представляет собой высокого уровня, минималистичный декларативный HTTP вызывающий API фреймворк.
В отличие от использования HttpClient вы больше не будете писать множество повторяющегося кода, а сможете отправлять HTTP запросы как локальные методы.
Forest имеет следующие характеристики:------
Пример основан на Spring Boot### Шаг 1: Добавление Maven зависимостей
Добавьте следующую зависимость Maven:
<dependency>
<groupId>com.dtflys.forest</groupId>
<artifactId>forest-spring-boot-starter</artifactId>
<version>1.6.4</version>
</dependency>
Для примера возьмём API карт Google Maps.
package com.yoursite.client;
import com.dtflys.forest.annotation.Request;
import com.dtflys.forest.annotation.DataParam;
public interface AmapClient {
/**
* Умный читатель наверняка заметил, что аннотация @Get указывает метод как GET запрос.
* {0} в URL ссылается на первый параметр, а {1} — на второй.
*/
@Get("http://ditu.amap.com/service/regeo?longitude={0}&latitude={1}")
Map getLocation(String longitude, String latitude);
}
Добавьте аннотацию @ForestScan
в конфигурационном или запускающем классе Spring Boot и укажите пакет с вашими интерфейсами.
@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Теперь вы можете вызывать ваши интерфейсы.
// Внедрение экземпляра интерфейса
@Autowired
private AmapClient amapClient;
...
// Вызов интерфейса
Map result = amapClient.getLocation("121.475078", "31.223577");
System.out.println(result);
/**
* Преобразование объекта параметра в JSON строку и передача её в теле запроса.
*/
@Post("/register")
String registerUser(@JSONBody MyUser user);
/**
* Преобразование параметра типа Map в JSON строку и передача её в теле запроса.
*/
@Post("/test/json")
String postJsonMap(@JSONBody Map mapObj);
``````markdown
## Отправка данных в формате JSON
```java
/**
* Прямое использование JSON строки и передача её в теле запроса.
*/
@Post("/test/json")
String postJsonText(@JSONBody String jsonText);
/**
* Преобразование объекта, помеченного JAXB аннотациями, в XML строку и передача её в теле запроса.
*/
@Post("/message")
String sendXmlMessage(@XMLBody MyMessage message);
/**
* Прямое использование XML строки и передача её в теле запроса.
*/
@Post("/test/xml")
String postXmlBodyString(@XMLBody String xml);
/**
* ProtobufProto.MyMessage — это сгенерированный класс данных для протокола Protobuf.
* Преобразование объекта данных, созданного с помощью Protobuf, в байтовый поток в формате Protobuf,
* который затем помещается в тело запроса для передачи.
*
* Примечание: требуется наличие зависимости от google protobuf.
*/
@Post(url = "/message", contentType = "application/octet-stream")
String sendProtobufMessage(@ProtobufBody ProtobufProto.MyMessage message);
/**
* Объект параметра для загрузки помечен аннотацией @DataFile.
* Параметр OnProgress представляет собой обратный вызов для отслеживания прогресса загрузки.
*/
@Post("/upload")
Map upload(@DataFile("file") String filePath, OnProgress onProgress);
Можно использовать один метод вместе с Lambda для решения задачи загрузки файла и отслеживания прогресса загрузки:
Map result = myClient.upload("D:\\TestUpload\\xxx.jpg", progress -> {
System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // Процент загруженных данных
if (progress.isDone()) { // Загружено ли всё?
System.out.println("-------- Upload Completed! --------");
}
});
```java
/**
* Загрузка списка файлов, упакованного в Map, где {_key} представляет ключ текущего цикла итерации
*/
@Post("/upload")
ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "{_key}") Map<String, byte[]> byteArrayMap);
/**
* Загрузка списка файлов, упакованного в List, где {_index} представляет индекс текущего цикла итерации (начиная с нуля)
*/
@Post("/upload")
ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-{_index}.jpg") List<byte[]> byteArrayList);
Загрузка файла также проста:
/**
* Аннотация @DownloadFile указана над методом
* Атрибут dir указывает директорию, куда будет загружен файл
* Параметр OnProgress представляет собой обратный вызов для отслеживания прогресса загрузки
* {0} представляет первый параметр
*/
@Get("http://localhost:8080/images/xxx.jpg")
@DownloadFile(dir = "{0}")
File downloadFile(String dir, OnProgress onProgress);
Код для вызова интерфейса загрузки и отслеживания прогресса загрузки:
File file = myClient.downloadFile("D:\\TestDownload", progress -> {
System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%"); // Процент загруженных данных
if (progress.isDone()) { // Загружено ли всё?
System.out.println("-------- Download Completed! --------");
}
});
@Post("/hello/user?username={username}")
@BasicAuth(username = "{username}", password = "bar")
String send(@DataVariable("username") String username);
@OAuth2(
tokenUri = "/auth/oauth/token",
clientId = "password",
clientSecret = "xxxxx-yyyyy-zzzzz",
grantType = OAuth2.GrantType.PASSWORD,
scope = "any",
username = "root",
password = "xxxxxx"
)
@Get("/test/data")
String getData();
```## Пользовательские аннотации
Forest позволяет вам самостоятельно определять аннотации в зависимости от ваших потребностей, что делает решение различных задач простым и элегантным, а также значительно расширяет возможности Forest.
### Определение аннотации
```java
/**
* Используйте пользовательскую аннотацию Forest для реализации подписи с шифрованием
* Все методы или интерфейсы, помеченные этой аннотацией, будут выполнять процесс шифрования подписи
* Процесс шифрования подписи будет обрабатываться жизненным циклом, указанным в аннотации @MethodLifeCycle
* Эту аннотацию можно использовать как для классов, так и для методов
*/
@Documented
/** Ключевой момент: Аннотация @MethodLifeCycle указывает класс жизненного цикла для данной аннотации */
@MethodLifeCycle(MyAuthLifeCycle.class)
@RequestAttributes
@Retention(RetentionPolicy.RUNTIME)
/** Указывает, что данная аннотация может использоваться для классов или методов */
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAuth {
/**
* Атрибут пользовательской аннотации: имя пользователя
* Все атрибуты пользовательских аннотаций могут быть получены в классе жизненного цикла
*/
String username();
/**
* Атрибут пользовательской аннотации: пароль
* Все атрибуты пользовательских аннотаций могут быть получены в классе жизненного цикла
*/
String password();
}
```### Определение класса жизненного цикла аннотации```java
/**
* Класс MyAuthLifeCycle является классом жизненного цикла для пользовательской аннотации @MyAuth.
* Поскольку @MyAuth предназначен для каждого запроса метода, он реализует интерфейс MethodAnnotationLifeCycle.
* Интерфейс MethodAnnotationLifeCycle имеет параметры типа generics.
* Первый параметр типа generics представляет тип аннотации, связанной с данным классом жизненного цикла.
* Второй параметр типа generics представляет тип данных, возвращаемых методами запроса, здесь используется Object для максимальной гибкости.
*/
public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> {
/**
* При вызове метода вызывается этот метод, пока ещё не выполнено отправление запроса.
* Этот метод позволяет получить информацию о вызове метода, соответствующего запросу,
* а также динамически переданные параметры вызова метода.
*/
@Override
public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
System.out.println("Вызов метода '" + method.getMethodName() + "' Аргументы: " + args);
}
/**
* Вызовите этот метод перед отправкой запроса, аналогично промежуточному обработчику.
*/
@Override
public boolean beforeExecute(ForestRequest request) {
// Используйте метод getAttribute для получения значений атрибутов из пользовательских аннотаций.
// Первый параметр метода getAttribute — это объект request, второй — имя атрибута в аннотациях.
String username = (String) getAttribute(request, "username");
String password = (String) getAttribute(request, "password");
// Зашифровать данные с помощью Base64.
``````java
String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
// Добавьте зашифрованные данные в заголовок запроса MyAuthorization
request.addHeader("MyAuthorization", basic);
return true;
``````markdown
## Инициализация метода
```java
/**
* Этот метод вызывается при инициализации метода запроса
*/
@Override
public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {
System.out.println("Метод '" + method.getMethodName() + "' инициализирован, Аргументы: " + args);
}
/**
* Добавьте пользовательскую аннотацию @MyAuth к интерфейсу запроса
* Параметры аннотации могут быть шаблонами строк, которые будут динамически заполнены при вызове метода
* Также можно использовать жёстко заданные строки
*/
@Get("/hello/user?username={username}")
@MyAuth(username = "{username}", password = "bar")
String send(@DataVariable("username") String username);
Программный подход к созданию запросов в библиотеке Forest позволяет выполнять запросы с использованием цепочки вызовов, что делает процесс удобным, эффективным и компактным.
// GET-запрос для доступа к сайту Baidu
String baidu = Forest.get("http://www.baidu.com").execute(String.class);
// POST-запрос для регистрации пользователя
String result = Forest.post("/user/register")
.contentType("application/json")
.addBody("username", "公子骏")
.addBody("password", "12345678")
.execute(String.class);
Награждён как «наиболее популярный проект» на конкурсе открытых проектов OSC 2021 года
Сканируйте QR-код для подписки на официальный канал, затем нажмите кнопку «Войти в группу» в меню

```Они используют```-----------------------------------
Список компаний, использующих Forest (порядок незначителен)
<div>
<table>
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.huawei.com/cn/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_huawei.png" class="no-zoom" alt="Huawei"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.thebeastshop.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_thebeastshop.jpg" class="no-zoom" alt="The Beast Shop"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://zgh.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_geely.png" class="no-zoom" alt="Geely Group"></div></a></td>
</tr>
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.ictbda.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_ictbda.png" class="no-zoom" alt="Институт вычислительной техники Китайской академии наук"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.woshipm.com/" target="_blank"><div style="height: 58px; padding: 1px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #4470f5"><img height="56px" src="/doc/images/logo/logo_woshipm.webp" class="no-zoom" alt="Каждый может стать продукт-менеджером"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="http://tldt.```markdown
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="https://weidubim.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #222222;"><img height="40px" src="/doc/images/logo/logo_weidubim.png" class="no-zoom" alt="Мудрая Вселенная"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.yiring.com/" target="_blank"><div style="height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="60px" src="/doc/images/logo/logo_yiring.png" class="no-zoom" alt="壹润"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="http://gzsunrun.cn/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_gzsunrun.jpg" class="no-zoom" alt="尚融网络科技"></div></a></td>
</tr>
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.huafang-aiot.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_huafangzhilian.png" class="no-zoom" alt="华方智联"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.hyperchain.cn/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_hyperchain.png" class="no-zoom" alt="趣链科技"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.byai.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #2b58fa;"><img height="40px" src="/doc/images/logo/logo_byai." class="no-zoom" alt="ByAI"></div></a></td>
</tr>
``````markdown
<table>
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="http://www.datapps.cn/" target="_blank"><div style="height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;"><img height="60px" src="/doc/images/logo/logo_datapps.png" class="no-zoom" alt="DatApps"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://m.hibobi.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_hibobi.png" class="no-zoom" alt="Hibobi"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://hzqianqi.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_hzqianqi.png" class="no-zoom" alt="仟奇"></div></a></td>
</tr>
<tr>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.swifthealth.cn/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_swifthealth.png" class="no-zoom" alt="SwiftHealth"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="https://www.manyibar.com/" target="_blank"><div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff"><img height="40px" src="/doc/images/logo/logo_manyibar.png" class="no-zoom" alt="满意吧"></div></a></td>
<td style="width: 500px"><a style="cursor: pointer;" href="http://www.ue-one.com/" target="_blank"><div style="height: 60px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;"><img height="60px" src="/doc/images/logo/logo_ue-one.png" class="no-zoom" alt="UE-One"></div></a></td>
</tr>
</table>
``` <a style="cursor: pointer;" href="cn/" target="_blank">
<div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #333333;">
<img height="40px" src="/doc/images/logo/logo_xwsoft.png" class="no-zoom" alt="Xinwang Shixin"/>
</div>
</a>
</td>
<td style="width: 500px">
<a style="cursor: pointer;" href="http://www.ynjzh.com/" target="_blank">
<div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff">
<img height="40px" src="/doc/images/logo/logo_ynjzh.png" class="no-zoom" alt="Jia Zhi Hui Keji"/>
</div>
</a>
</td>
<td style="width: 500px">
<a style="cursor: pointer;" href="http://www.xingsnb.cn/" target="_blank">
<div style="height: 50px; padding: 5px; margin: 0; display: flex; justify-content: center; align-items: center; border-radius: 8px;background-color: #ffffff">
<img height="40px" src="/doc/images/logo/logo_xingsnb.jpg" class="no-zoom" alt="Xingsheng Gongshe"/>
</div>
</a>
</td>
</tr>
</table>
Примечание: Исходный текст был в основном на китайском языке с использованием английских символов для некоторых URL. В соответствии с правилами перевода, текст внутри атрибута `alt` был переведён на русский язык, остальные части оставлены без изменения. Участие в проекте
-----------------------------------1. Присоединиться к обсуждению в чате, где вы можете обсудить возникающие вопросы с другими участниками.
2. Создайте issue, если вопрос уже существует, вы можете назначить его себе. В противном случае создайте новый issue.
3. Fork репозиторий проекта.
4. Создайте новую ветку. Если это добавление нового функционала, используйте формат названия `feat_${номер_issue}`, а если это исправление ошибки — `fix_${номер_issue}`.
5. Локально протестируйте изменения, убедитесь, что все существующие юнит-тесты проходят успешно, а также добавьте новые тесты для проверки ваших изменений.
6. Отправьте свои изменения.
7. Создайте pull request.
8. Я проведу проверку и тестирование вашего pull request. Если всё будет хорошо, ваши изменения будут объединены в ветку `dev`, а затем включены в основной релиз.
Приглашаем всех участников активно создавать issues и pull requests. Те, чьи pull requests были приняты, будут отмечены в списке вкладчиков в README.
Лицензия проекта
--------------------------
Лицензия MIT
Авторское право © 2016 Юнь龚
(Авторское право © 2016 Юнь龚 остается без изменений, так как это имя автора.)
Вы можете оставить комментарий после Вход в систему
Неприемлемый контент может быть отображен здесь и не будет показан на странице. Вы можете проверить и изменить его с помощью соответствующей функции редактирования.
Если вы подтверждаете, что содержание не содержит непристойной лексики/перенаправления на рекламу/насилия/вульгарной порнографии/нарушений/пиратства/ложного/незначительного или незаконного контента, связанного с национальными законами и предписаниями, вы можете нажать «Отправить» для подачи апелляции, и мы обработаем ее как можно скорее.
Комментарии ( 0 )