spring 5 webclient使用指南

之前寫了一篇restTemplate使用實例洞就,由于spring 5全面引入reactive擅羞,同時也有了restTemplate的reactive版webclient轴脐,本文就來對應展示下webclient的基本使用堪藐。

請求攜帶header

  • 攜帶cookie
    @Test
    public void testWithCookie(){
        Mono<String> resp = WebClient.create()
                .method(HttpMethod.GET)
                .uri("http://baidu.com")
                .cookie("token","xxxx")
                .cookie("JSESSIONID","XXXX")
                .retrieve()
                .bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }
  • 攜帶basic auth
    @Test
    public void testWithBasicAuth(){
        String basicAuth = "Basic "+ Base64.getEncoder().encodeToString("user:pwd".getBytes(StandardCharsets.UTF_8));
        LOGGER.info(basicAuth);
        Mono<String> resp = WebClient.create()
                .get()
                .uri("http://baidu.com")
                .header(HttpHeaders.AUTHORIZATION,basicAuth)
                .retrieve()
                .bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }
  • 設置全局user-agent
    @Test
    public void testWithHeaderFilter(){
        WebClient webClient = WebClient.builder()
                .defaultHeader(HttpHeaders.USER_AGENT, "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36")
                .filter(ExchangeFilterFunctions
                        .basicAuthentication("user","password"))
                .filter((clientRequest, next) -> {
                    LOGGER.info("Request: {} {}", clientRequest.method(), clientRequest.url());
                    clientRequest.headers()
                            .forEach((name, values) -> values.forEach(value -> LOGGER.info("{}={}", name, value)));
                    return next.exchange(clientRequest);
                })
                .build();
        Mono<String> resp = webClient.get()
                .uri("https://baidu.com")
                .retrieve()
                .bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }

get請求

  • 使用placeholder傳遞參數
    @Test
    public void testUrlPlaceholder(){
        Mono<String> resp = WebClient.create()
                .get()
                //多個參數也可以直接放到map中,參數名與placeholder對應上即可
                .uri("http://www.baidu.com/s?wd={key}&other={another}","北京天氣","test") //使用占位符
                .retrieve()
                .bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());

    }
  • 使用uriBuilder傳遞參數
    @Test
    public void testUrlBiulder(){
        Mono<String> resp = WebClient.create()
                .get()
                .uri(uriBuilder -> uriBuilder
                        .scheme("http")
                        .host("www.baidu.com")
                        .path("/s")
                        .queryParam("wd", "北京天氣")
                        .queryParam("other", "test")
                        .build())
                .retrieve()
                .bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }

post表單

    @Test
    public void testFormParam(){
        MultiValueMap<String, String> formData = new LinkedMultiValueMap<>();
        formData.add("name1","value1");
        formData.add("name2","value2");
        Mono<String> resp = WebClient.create().post()
                .uri("http://www.w3school.com.cn/test/demo_form.asp")
                .contentType(MediaType.APPLICATION_FORM_URLENCODED)
                .body(BodyInserters.fromFormData(formData))
                .retrieve().bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }

post json

  • 使用bean來post
static class Book {
        String name;
        String title;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }
    }

    @Test
    public void testPostJson(){
        Book book = new Book();
        book.setName("name");
        book.setTitle("this is title");
        Mono<String> resp = WebClient.create().post()
                .uri("http://localhost:8080/demo/json")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(Mono.just(book),Book.class)
                .retrieve().bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }
  • 直接post raw json
    @Test
    public void testPostRawJson(){
        Mono<String> resp = WebClient.create().post()
                .uri("http://localhost:8080/demo/json")
                .contentType(MediaType.APPLICATION_JSON_UTF8)
                .body(BodyInserters.fromObject("{\n" +
                        "  \"title\" : \"this is title\",\n" +
                        "  \"author\" : \"this is author\"\n" +
                        "}"))
                .retrieve().bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }

post二進制--上傳文件

    @Test
    public void testUploadFile(){
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.IMAGE_PNG);
        HttpEntity<ClassPathResource> entity = new HttpEntity<>(new ClassPathResource("parallel.png"), headers);
        MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();
        parts.add("file", entity);
        Mono<String> resp = WebClient.create().post()
                .uri("http://localhost:8080/upload")
                .contentType(MediaType.MULTIPART_FORM_DATA)
                .body(BodyInserters.fromMultipartData(parts))
                .retrieve().bodyToMono(String.class);
        LOGGER.info("result:{}",resp.block());
    }

下載二進制

  • 下載圖片
    @Test
    public void testDownloadImage() throws IOException {
        Mono<Resource> resp = WebClient.create().get()
                .uri("http://www.toolip.gr/captcha?complexity=99&size=60&length=9")
                .accept(MediaType.IMAGE_PNG)
                .retrieve().bodyToMono(Resource.class);
        Resource resource = resp.block();
        BufferedImage bufferedImage = ImageIO.read(resource.getInputStream());
        ImageIO.write(bufferedImage, "png", new File("captcha.png"));

    }
  • 下載文件
    @Test
    public void testDownloadFile() throws IOException {
        Mono<ClientResponse> resp = WebClient.create().get()
                .uri("http://localhost:8080/file/download")
                .accept(MediaType.APPLICATION_OCTET_STREAM)
                .exchange();
        ClientResponse response = resp.block();
        String disposition = response.headers().asHttpHeaders().getFirst(HttpHeaders.CONTENT_DISPOSITION);
        String fileName = disposition.substring(disposition.indexOf("=")+1);
        Resource resource = response.bodyToMono(Resource.class).block();
        File out = new File(fileName);
        FileUtils.copyInputStreamToFile(resource.getInputStream(),out);
        LOGGER.info(out.getAbsolutePath());
    }

錯誤處理

    @Test
    public void testRetrieve4xx(){
        WebClient webClient = WebClient.builder()
                .baseUrl("https://api.github.com")
                .defaultHeader(HttpHeaders.CONTENT_TYPE, "application/vnd.github.v3+json")
                .defaultHeader(HttpHeaders.USER_AGENT, "Spring 5 WebClient")
                .build();
        WebClient.ResponseSpec responseSpec = webClient.method(HttpMethod.GET)
                .uri("/user/repos?sort={sortField}&direction={sortDirection}",
                        "updated", "desc")
                .retrieve();
        Mono<String> mono = responseSpec
                .onStatus(e -> e.is4xxClientError(),resp -> {
                    LOGGER.error("error:{},msg:{}",resp.statusCode().value(),resp.statusCode().getReasonPhrase());
                    return Mono.error(new RuntimeException(resp.statusCode().value() + " : " + resp.statusCode().getReasonPhrase()));
                })
                .bodyToMono(String.class)
                .doOnError(WebClientResponseException.class, err -> {
                    LOGGER.info("ERROR status:{},msg:{}",err.getRawStatusCode(),err.getResponseBodyAsString());
                    throw new RuntimeException(err.getMessage());
                })
                .onErrorReturn("fallback");
        String result = mono.block();
        LOGGER.info("result:{}",result);
    }
  • 可以使用onStatus根據status code進行異常適配
  • 可以使用doOnError異常適配
  • 可以使用onErrorReturn返回默認值

小結

webclient是新一代的async rest template滋恬,api也相對簡潔匀哄,而且是reactive的秦效,非常值得使用。

doc

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末涎嚼,一起剝皮案震驚了整個濱河市阱州,隨后出現的幾起案子,更是在濱河造成了極大的恐慌法梯,老刑警劉巖苔货,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現場離奇詭異立哑,居然都是意外死亡夜惭,警方通過查閱死者的電腦和手機,發(fā)現死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門铛绰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诈茧,“玉大人,你說我怎么就攤上這事至耻∪糁澹” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵尘颓,是天一觀的道長走触。 經常有香客問我,道長疤苹,這世上最難降的妖魔是什么互广? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮卧土,結果婚禮上惫皱,老公的妹妹穿的比我還像新娘。我一直安慰自己尤莺,他們只是感情好旅敷,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著颤霎,像睡著了一般媳谁。 火紅的嫁衣襯著肌膚如雪涂滴。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天晴音,我揣著相機與錄音柔纵,去河邊找鬼。 笑死锤躁,一個胖子當著我的面吹牛搁料,可吹牛的內容都是我干的。 我是一名探鬼主播系羞,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼郭计,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了觉啊?” 一聲冷哼從身側響起拣宏,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤沈贝,失蹤者是張志新(化名)和其女友劉穎杠人,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體宋下,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡嗡善,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了学歧。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片罩引。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖枝笨,靈堂內的尸體忽然破棺而出袁铐,到底是詐尸還是另有隱情,我是刑警寧澤横浑,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布剔桨,位于F島的核電站,受9級特大地震影響徙融,放射性物質發(fā)生泄漏洒缀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一欺冀、第九天 我趴在偏房一處隱蔽的房頂上張望树绩。 院中可真熱鬧,春花似錦隐轩、人聲如沸饺饭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瘫俊。三九已至蛛芥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間军援,已是汗流浹背仅淑。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留胸哥,地道東北人涯竟。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像空厌,于是被迫代替她去往敵國和親庐船。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容