1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/dromara-forest

Клонировать/Скачать
Внести вклад в разработку кода
Синхронизировать код
Отмена
Подсказка: Поскольку Git не поддерживает пустые директории, создание директории приведёт к созданию пустого файла .keep.
Loading...
README.md

Логотип

JDK Лицензия Документация Автор

Forest - декларативный HTTP клиентский фреймворк

Проект представляет собой высокого уровня, минималистичный декларативный HTTP вызывающий API фреймворк.
В отличие от использования HttpClient вы больше не будете писать множество повторяющегося кода, а сможете отправлять HTTP запросы как локальные методы.

Награды

  • 2021 год - самый популярный проект в конкурсе "OSChina Open Source Project Selection"
  • 2022 год - самый горячий проект в сообществе "OSChina Open Source Project Selection"

Документация и примеры:

Forest имеет следующие характеристики:------

  • Поддерживает одновременно программный и декларативный методы отправки запросов
  • Использует HttpClient и OkHttp в качестве основных библиотек
  • Отправляет HTTP-запросы путём вызова локальных методов, что позволяет разделить бизнес-логику от протокола HTTP
  • Поскольку предназначен для работы с внешними API, он не требует зависимости от Spring Cloud или других регистрирующих центров
  • Поддерживает все методы запросов: GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
  • Поддерживает загрузку и скачивание файлов
  • Обеспечивает гибкие шаблонные выражения
  • Поддерживает middleware для обработки различных этапов жизненного цикла запроса
  • Поддерживает пользовательские аннотации
  • Поддерживает аутентификацию OAuth2
  • Поддерживает фильтры для фильтрации входящих данных
  • Определяет HTTP-запросы через аннотации и конфигурационные файлы
  • Поддерживает интеграцию со Spring и Spring Boot
  • Поддерживает сериализацию и десериализацию данных в форматах JSON, XML и Protocol Buffers
  • Расширяемые и заменяемые преобразователи данных между типами JSON, XML или другими типами
  • Поддерживает фреймворки преобразования JSON: Fastjson2, Fastjson1, Jackson, Gson
  • Поддерживает преобразование XML в JAXB-формате
  • Реализует обратные вызовы для успешных и ошибочных запросов через параметры onSuccess и onError
  • Простая конфигурация, обычно требуется всего одна аннотация @Request для определения большинства запросов
  • Поддерживает асинхронные запросы* Поддерживает SSE (Server-Sent Events) Быстрый старт

Пример основан на Spring Boot### Шаг 1: Добавление Maven зависимостей

Добавьте следующую зависимость Maven:

<dependency>
    <groupId>com.dtflys.forest</groupId>
    <artifactId>forest-spring-boot-starter</artifactId>
    <version>1.6.4</version>
</dependency>

Шаг 2: Создание интерфейса

Для примера возьмём 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);
}

Шаг 3: Сканирование интерфейсов

Добавьте аннотацию @ForestScan в конфигурационном или запускающем классе Spring Boot и укажите пакет с вашими интерфейсами.

@SpringBootApplication
@Configuration
@ForestScan(basePackages = "com.yoursite.client")
public class MyApplication {
  public static void main(String[] args) {
      SpringApplication.run(MyApplication.class, args);
   }
}

Шаг 4: Вызов интерфейсов

Теперь вы можете вызывать ваши интерфейсы.

// Внедрение экземпляра интерфейса
@Autowired
private AmapClient amapClient;
...
// Вызов интерфейса
Map result = amapClient.getLocation("121.475078", "31.223577");
System.out.println(result);

Отправка JSON данных

/**
 * Преобразование объекта параметра в 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);

Отправка данных в формате XML

/**
 * Преобразование объекта, помеченного JAXB аннотациями, в XML строку и передача её в теле запроса.
 */
@Post("/message")
String sendXmlMessage(@XMLBody MyMessage message);

/**
 * Прямое использование XML строки и передача её в теле запроса.
 */
@Post("/test/xml")
String postXmlBodyString(@XMLBody String xml);

Отправка данных в формате Protobuf

/**
 * 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);

OAuth 2.0

@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-код для подписки на официальный канал, затем нажмите кнопку «Войти в группу» в меню

![avatar](doc/images/qr_code.jpg)
```Они используют```-----------------------------------
Список компаний, использующих 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 )

Вы можете оставить комментарий после Вход в систему

Введение

Декларативный HTTP-клиент API-фреймворк делает отправку HTTP/HTTPS-запросов на Java проще. Он предоставляет более высокий уровень абстракции по сравнению с OkHttp и HttpClient и является удобным инструментом для инкапсуляции вызовов сторонних RESTful API клиентских интерфейсов. Это ещё один вариант помимо Retrofit и Feign. Конфигурация интерфейс... Развернуть Свернуть
MIT
Отмена

Обновления (78)

все

Участники

все

Недавние действия

Загрузить больше
Больше нет результатов для загрузки
1
https://api.gitlife.ru/oschina-mirror/dromara-forest.git
git@api.gitlife.ru:oschina-mirror/dromara-forest.git
oschina-mirror
dromara-forest
dromara-forest
master