1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Forest輕量級(jí) HTTP 客戶端

        聯(lián)合創(chuàng)作 · 2023-09-29 07:38

        Forest是一個(gè)高層的、極簡(jiǎn)的輕量級(jí)HTTP調(diào)用API框架。
        相比于直接使用Httpclient您不再用寫(xiě)一大堆重復(fù)的代碼了,而是像調(diào)用本地方法一樣去發(fā)送HTTP請(qǐng)求。

        文檔和示例:

        Forest有哪些特性?

        • 以Httpclient和OkHttp為后端框架
        • 通過(guò)調(diào)用本地方法的方式去發(fā)送Http請(qǐng)求, 實(shí)現(xiàn)了業(yè)務(wù)邏輯與Http協(xié)議之間的解耦
        • 因?yàn)獒槍?duì)第三方接口,所以不需要依賴Spring Cloud和任何注冊(cè)中心
        • 支持所有請(qǐng)求方法:GET, HEAD, OPTIONS, TRACE, POST, DELETE, PUT, PATCH
        • 支持文件上傳和下載
        • 支持靈活的模板表達(dá)式
        • 支持?jǐn)r截器處理請(qǐng)求的各個(gè)生命周期
        • 支持自定義注解
        • 支持OAuth2驗(yàn)證
        • 支持過(guò)濾器來(lái)過(guò)濾傳入的數(shù)據(jù)
        • 基于注解、配置化的方式定義Http請(qǐng)求
        • 支持Spring和Springboot集成
        • JSON字符串到Java對(duì)象的自動(dòng)化解析
        • XML文本到Java對(duì)象的自動(dòng)化解析
        • JSON、XML或其他類型轉(zhuǎn)換器可以隨意擴(kuò)展和替換
        • 支持JSON轉(zhuǎn)換框架: Fastjson, Jackson, Gson
        • 支持JAXB形式的XML轉(zhuǎn)換
        • 可以通過(guò)OnSuccess和OnError接口參數(shù)實(shí)現(xiàn)請(qǐng)求結(jié)果的回調(diào)
        • 配置簡(jiǎn)單,一般只需要@Request一個(gè)注解就能完成絕大多數(shù)請(qǐng)求的定義
        • 支持異步請(qǐng)求調(diào)用

        極速開(kāi)始

        以下例子基于Spring Boot

        第一步:添加Maven依賴

        直接添加以下maven依賴即可

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

        第二步:創(chuàng)建一個(gè)interface

        就以高德地圖API為栗子吧

        package com.yoursite.client;
        
        import com.dtflys.forest.annotation.Request;
        import com.dtflys.forest.annotation.DataParam;
        
        public interface AmapClient {
        
            /**
             * 聰明的你一定看出來(lái)了@Get注解代表該方法專做GET請(qǐng)求
             * 在url中的${0}代表引用第一個(gè)參數(shù),${1}引用第二個(gè)參數(shù)
             */
            @Get("http://ditu.amap.com/service/regeo?longitude=${0}&latitude=${1}")
            Map getLocation(String longitude, String latitude);
        }
        

        第三步:掃描接口

        在Spring Boot的配置類或者啟動(dòng)類上加上@ForestScan注解,并在basePackages屬性里填上遠(yuǎn)程接口的所在的包名

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

        第四步:調(diào)用接口

        OK,我們可以愉快地調(diào)用接口了

        // 注入接口實(shí)例
        @Autowired
        private AmapClient amapClient;
        ...
        // 調(diào)用接口
        Map result = amapClient.getLocation("121.475078", "31.223577");
        System.out.println(result);

        發(fā)送JSON數(shù)據(jù)

        /**
         * 將對(duì)象參數(shù)解析為JSON字符串,并放在請(qǐng)求的Body進(jìn)行傳輸
         */
        @Post("/register")
        String registerUser(@JSONBody MyUser user);
        
        /**
         * 將Map類型參數(shù)解析為JSON字符串,并放在請(qǐng)求的Body進(jìn)行傳輸
         */
        @Post("/test/json")
        String postJsonMap(@JSONBody Map mapObj);
        
        /**
         * 直接傳入一個(gè)JSON字符串,并放在請(qǐng)求的Body進(jìn)行傳輸
         */
        @Post("/test/json")
        String postJsonText(@JSONBody String jsonText);

        發(fā)送XML數(shù)據(jù)

        /**
         * 將一個(gè)通過(guò)JAXB注解修飾過(guò)的類型對(duì)象解析為XML字符串
         * 并放在請(qǐng)求的Body進(jìn)行傳輸
         */
        @Post("/message")
        String sendXmlMessage(@XMLBody MyMessage message);
        
        /**
         * 直接傳入一個(gè)XML字符串,并放在請(qǐng)求的Body進(jìn)行傳輸
         */
        @Post("/test/xml")
        String postXmlBodyString(@XMLBody String xml);

        文件上傳

        /**
         * 用@DataFile注解修飾要上傳的參數(shù)對(duì)象
         * OnProgress參數(shù)為監(jiān)聽(tīng)上傳進(jìn)度的回調(diào)函數(shù)
         */
        @Post("/upload")
        Map upload(@DataFile("file") String filePath, OnProgress onProgress);

        可以用一個(gè)方法加Lambda同時(shí)解決文件上傳和上傳的進(jìn)度監(jiān)聽(tīng)

        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!   --------");
            }
        });

        多文件批量上傳

        /**
         * 上傳Map包裝的文件列表,其中 ${_key} 代表Map中每一次迭代中的鍵值
         */
        @Post("/upload")
        ForestRequest<Map> uploadByteArrayMap(@DataFile(value = "file", fileName = "${_key}") Map<String, byte[]> byteArrayMap);
        
        /**
         * 上傳List包裝的文件列表,其中 ${_index} 代表每次迭代List的循環(huán)計(jì)數(shù)(從零開(kāi)始計(jì))
         */
        @Post("/upload")
        ForestRequest<Map> uploadByteArrayList(@DataFile(value = "file", fileName = "test-img-${_index}.jpg") List<byte[]> byteArrayList);

        文件下載

        下載文件也是同樣的簡(jiǎn)單

        /**
         * 在方法上加上@DownloadFile注解
         * dir屬性表示文件下載到哪個(gè)目錄
         * OnProgress參數(shù)為監(jiān)聽(tīng)上傳進(jìn)度的回調(diào)函數(shù)
         * ${0}代表引用第一個(gè)參數(shù)
         */
        @Get("http://localhost:8080/images/xxx.jpg")
        @DownloadFile(dir = "${0}")
        File downloadFile(String dir, OnProgress onProgress);

        調(diào)用下載接口以及監(jiān)聽(tīng)下載進(jìn)度的代碼如下:

        File file = myClient.downloadFile("D:\\TestDownload", progress -> {
            System.out.println("progress: " + Math.round(progress.getRate() * 100) + "%");  // 已下載百分比
            if (progress.isDone()) {   // 是否下載完成
                System.out.println("--------   Download Completed!   --------");
            }
        });

        基本簽名驗(yàn)證

        @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允許您根據(jù)需要自行定義注解,不但讓您可以簡(jiǎn)單優(yōu)雅得解決各種需求,而且極大得擴(kuò)展了Forest的能力。

        定義一個(gè)注解

        /**
         * 用Forest自定義注解實(shí)現(xiàn)一個(gè)自定義的簽名加密注解
         * 凡用此接口修飾的方法或接口,其對(duì)應(yīng)的所有請(qǐng)求都會(huì)執(zhí)行自定義的簽名加密過(guò)程
         * 而自定義的簽名加密過(guò)程,由這里的@MethodLifeCycle注解指定的生命周期類進(jìn)行處理
         * 可以將此注解用在接口類和方法上
         */
        @Documented
        /** 重點(diǎn): @MethodLifeCycle注解指定該注解的生命周期類*/
        @MethodLifeCycle(MyAuthLifeCycle.class)
        @RequestAttributes
        @Retention(RetentionPolicy.RUNTIME)
        /** 指定該注解可用于類上或方法上 */
        @Target({ElementType.TYPE, ElementType.METHOD})
        public @interface MyAuth {
        
            /** 
             * 自定義注解的屬性:用戶名
             * 所有自定注解的屬性可以在生命周期類中被獲取到
             */
            String username();
        
            /** 
             * 自定義注解的屬性:密碼
             * 所有自定注解的屬性可以在生命周期類中被獲取到
             */
            String password();
        }

        定義注解生命周期類

        /**
         *  MyAuthLifeCycle 為自定義的 @MyAuth 注解的生命周期類
         * 因?yàn)?@MyAuth 是針對(duì)每個(gè)請(qǐng)求方法的,所以它實(shí)現(xiàn)自 MethodAnnotationLifeCycle 接口
         * MethodAnnotationLifeCycle 接口帶有泛型參數(shù)
         * 第一個(gè)泛型參數(shù)是該生命周期類綁定的注解類型
         * 第二個(gè)泛型參數(shù)為請(qǐng)求方法返回的數(shù)據(jù)類型,為了盡可能適應(yīng)多的不同方法的返回類型,這里使用 Object
         */
        public class MyAuthLifeCycle implements MethodAnnotationLifeCycle<MyAuth, Object> {
        
         
            /**
             * 當(dāng)方法調(diào)用時(shí)調(diào)用此方法,此時(shí)還沒(méi)有執(zhí)行請(qǐng)求發(fā)送
             * 次方法可以獲得請(qǐng)求對(duì)應(yīng)的方法調(diào)用信息,以及動(dòng)態(tài)傳入的方法調(diào)用參數(shù)列表
             */
            @Override
            public void onInvokeMethod(ForestRequest request, ForestMethod method, Object[] args) {
                System.out.println("Invoke Method '" + method.getMethodName() + "' Arguments: " + args);
            }
        
            /**
             * 發(fā)送請(qǐng)求前執(zhí)行此方法,同攔截器中的一樣
             */
            @Override
            public boolean beforeExecute(ForestRequest request) {
                // 通過(guò)getAttribute方法獲取自定義注解中的屬性值
                // getAttribute第一個(gè)參數(shù)為request對(duì)象,第二個(gè)參數(shù)為自定義注解中的屬性名
                String username = (String) getAttribute(request, "username");
                String password = (String) getAttribute(request, "password");
                // 使用Base64進(jìn)行加密
                String basic = "MyAuth " + Base64Utils.encode("{" + username + ":" + password + "}");
                // 調(diào)用addHeader方法將加密結(jié)構(gòu)加到請(qǐng)求頭MyAuthorization中
                request.addHeader("MyAuthorization", basic);
                return true;
            }
        
            /**
             * 此方法在請(qǐng)求方法初始化的時(shí)候被調(diào)用
             */
            @Override
            public void onMethodInitialized(ForestMethod method, BasicAuth annotation) {
                System.out.println("Method '" + method.getMethodName() + "' Initialized, Arguments: " + args);
            }
        }

        使用自定義的注解

        /**
         * 在請(qǐng)求接口上加上自定義的 @MyAuth 注解
         * 注解的參數(shù)可以是字符串模板,通過(guò)方法調(diào)用的時(shí)候動(dòng)態(tài)傳入
         * 也可以是寫(xiě)死的字符串
         */
        @Get("/hello/user?username=${username}")
        @MyAuth(username = "${username}", password = "bar")
        String send(@DataVariable("username") String username);

        詳細(xì)文檔請(qǐng)看:http://forest.dtflyx.com/

        瀏覽 26
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        編輯 分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            96精品久久久久久蜜臀浪 | 天天撸天天色 | 超碰97成人无码中文字幕 | 操逼专区| 骚网站 | 国产传媒三级 | www.看黄 | 女同学玩裸体聚会 | 午夜爱爱爱| 免费的黄色小视频 |