序
本文主要研究一下java11的httpclient的基本使用。
变化
从java9的jdk.incubator.httpclient模块迁移到java.net.http模块,包名由jdk.incubator.http改为java.net.http 原来的诸如httpresponse.bodyhandler.asstring()方法变更为httpresponse.bodyhandlers.ofstring(),变化一为bodyhandler改为bodyhandlers,变化二为asxxx()之类的方法改为ofxxx(),由as改为of实例
设置超时时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@test public void testtimeout() throws ioexception, interruptedexception { //1.set connect timeout httpclient client = httpclient.newbuilder() .connecttimeout(duration.ofmillis( 5000 )) .followredirects(httpclient.redirect.normal) .build();
//2.set read timeout httprequest request = httprequest.newbuilder() .uri(uri.create( "http://openjdk.java.net/" )) .timeout(duration.ofmillis( 5009 )) .build();
httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring());
system.out.println(response.body());
} |
httpconnecttimeoutexception实例
1 2 3 4 5 6 7 |
caused by: java.net.http.httpconnecttimeoutexception: http connect timed out at java.net.http/jdk.internal.net.http.responsetimerevent.handle(responsetimerevent.java: 68 ) at java.net.http/jdk.internal.net.http.httpclientimpl.purgetimeoutsandreturnnextdeadline(httpclientimpl.java: 1248 ) at java.net.http/jdk.internal.net.http.httpclientimpl$selectormanager.run(httpclientimpl.java: 877 ) caused by: java.net.connectexception: http connect timed out at java.net.http/jdk.internal.net.http.responsetimerevent.handle(responsetimerevent.java: 69 ) ... 2 more |
httptimeoutexception实例
1 2 3 4 5 |
java.net.http.httptimeoutexception: request timed out
at java.net.http/jdk.internal.net.http.httpclientimpl.send(httpclientimpl.java: 559 ) at java.net.http/jdk.internal.net.http.httpclientfacade.send(httpclientfacade.java: 119 ) at com.example.httpclienttest.testtimeout(httpclienttest.java: 40 ) |
设置authenticator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
@test public void testbasicauth() throws ioexception, interruptedexception { httpclient client = httpclient.newbuilder() .connecttimeout(duration.ofmillis( 5000 )) .authenticator( new authenticator() { @override protected passwordauthentication getpasswordauthentication() { return new passwordauthentication( "admin" , "password" .tochararray()); } }) .build();
httprequest request = httprequest.newbuilder() .uri(uri.create( "http://localhost:8080/json/info" )) .timeout(duration.ofmillis( 5009 )) .build();
httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring());
system.out.println(response.statuscode()); system.out.println(response.body()); } |
设置header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
@test public void testcookies() throws ioexception, interruptedexception { httpclient client = httpclient.newbuilder() .connecttimeout(duration.ofmillis( 5000 )) .build(); httprequest request = httprequest.newbuilder() .uri(uri.create( "http://localhost:8080/json/cookie" )) .header( "cookie" , "jsessionid=4f994730-32d7-4e22-a18b-25667ddeb636; userid=java11" ) .timeout(duration.ofmillis( 5009 )) .build(); httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring());
system.out.println(response.statuscode()); system.out.println(response.body()); } |
通过request可以自己设置header
get
同步
1 2 3 4 5 6 7 8 9 10 11 12 |
@test public void testsyncget() throws ioexception, interruptedexception { httpclient client = httpclient.newhttpclient(); httprequest request = httprequest.newbuilder() .uri(uri.create( "https://www.baidu.com" )) .build();
httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring());
system.out.println(response.body()); } |
异步
1 2 3 4 5 6 7 8 9 10 11 |
@test public void testasyncget() throws executionexception, interruptedexception { httpclient client = httpclient.newhttpclient(); httprequest request = httprequest.newbuilder() .uri(uri.create( "https://www.baidu.com" )) .build();
completablefuture<string> result = client.sendasync(request, httpresponse.bodyhandlers.ofstring()) .thenapply(httpresponse::body); system.out.println(result.get()); } |
post表单
1 2 3 4 5 6 7 8 9 10 11 12 |
@test public void testpostform() throws ioexception, interruptedexception { httpclient client = httpclient.newbuilder().build(); httprequest request = httprequest.newbuilder() .uri(uri.create( "http://www.w3school.com.cn/demo/demo_form.asp" )) .header( "content-type" , "application/x-www-form-urlencoded" ) .post(httprequest.bodypublishers.ofstring( "name1=value1&name2=value2" )) .build();
httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring()); system.out.println(response.statuscode()); } |
header指定内容是表单类型,然后通过bodypublishers.ofstring传递表单数据,需要自己构建表单参数
post json
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
@test public void testpostjsongetjson() throws executionexception, interruptedexception, jsonprocessingexception { objectmapper objectmapper = new objectmapper(); stockdto dto = new stockdto(); dto.setname( "hj" ); dto.setsymbol( "hj" ); dto.settype(stockdto.stocktype.sh); string requestbody = objectmapper .writerwithdefaultprettyprinter() .writevalueasstring(dto);
httprequest request = httprequest.newbuilder(uri.create( "http://localhost:8080/json/demo" )) .header( "content-type" , "application/json" ) .post(httprequest.bodypublishers.ofstring(requestbody)) .build();
completablefuture<stockdto> result = httpclient.newhttpclient() .sendasync(request, httpresponse.bodyhandlers.ofstring()) .thenapply(httpresponse::body) .thenapply(body -> { try { return objectmapper.readvalue(body,stockdto. class ); } catch (ioexception e) { return new stockdto(); } }); system.out.println(result.get()); } |
post json的话,body自己json化为string,然后header指定是json格式
文件上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
@test public void testuploadfile() throws ioexception, interruptedexception, urisyntaxexception { httpclient client = httpclient.newhttpclient(); path path = path.of(getclass().getclassloader().getresource( "body.txt" ).touri()); file file = path.tofile();
string multipartformdataboundary = "java11httpclientformboundary" ; org.apache.http.httpentity multipartentity = multipartentitybuilder.create() .addpart( "file" , new filebody(file, contenttype.default_binary)) .setboundary(multipartformdataboundary) //要设置,否则阻塞 .build();
httprequest request = httprequest.newbuilder() .uri(uri.create( "http://localhost:8080/file/upload" )) .header( "content-type" , "multipart/form-data; boundary=" + multipartformdataboundary) .post(httprequest.bodypublishers.ofinputstream(() -> { try { return multipartentity.getcontent(); } catch (ioexception e) { e.printstacktrace(); throw new runtimeexception(e); } })) .build();
httpresponse<string> response = client.send(request, httpresponse.bodyhandlers.ofstring());
system.out.println(response.body()); } |
文件下载
1 2 3 4 5 6 7 8 9 10 11 |
@test public void testasyncdownload() throws executionexception, interruptedexception { httpclient client = httpclient.newhttpclient(); httprequest request = httprequest.newbuilder() .uri(uri.create( "http://localhost:8080/file/download" )) .build();
completablefuture<path> result = client.sendasync(request, httpresponse.bodyhandlers.offile(paths.get( "/tmp/body.txt" ))) .thenapply(httpresponse::body); system.out.println(result.get()); } |
使用httpresponse.bodyhandlers.offile来接收文件
并发请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@test public void testconcurrentrequests(){ httpclient client = httpclient.newhttpclient(); list<string> urls = list.of( "http://www.baidu.com" , "http://www.alibaba.com/" , "http://www.tencent.com" ); list<httprequest> requests = urls.stream() .map(url -> httprequest.newbuilder(uri.create(url))) .map(reqbuilder -> reqbuilder.build()) .collect(collectors.tolist());
list<completablefuture<httpresponse<string>>> futures = requests.stream() .map(request -> client.sendasync(request, httpresponse.bodyhandlers.ofstring())) .collect(collectors.tolist()); futures.stream() .foreach(e -> e.whencomplete((resp,err) -> { if (err != null ){ err.printstacktrace(); } else { system.out.println(resp.body()); system.out.println(resp.statuscode()); } })); completablefuture.allof(futures .toarray(completablefuture<?>[]:: new )) .join(); } |
错误处理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
@test public void testhandleexception() throws executionexception, interruptedexception { httpclient client = httpclient.newbuilder() .connecttimeout(duration.ofmillis( 5000 )) .build(); httprequest request = httprequest.newbuilder() .uri(uri.create( "https://twitter.com" )) .build();
completablefuture<string> result = client.sendasync(request, httpresponse.bodyhandlers.ofstring()) // .whencomplete((resp,err) -> { // if(err != null){ // err.printstacktrace(); // }else{ // system.out.println(resp.body()); // system.out.println(resp.statuscode()); // } // }) .thenapply(httpresponse::body) .exceptionally(err -> { err.printstacktrace(); return "fallback" ; }); system.out.println(result.get()); } |
http2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
@test public void testhttp2() throws urisyntaxexception { httpclient.newbuilder() .followredirects(httpclient.redirect.never) .version(httpclient.version.http_2) .build() .sendasync(httprequest.newbuilder() .uri( new uri( "https://http2.akamai.com/demo" )) .get() .build(), httpresponse.bodyhandlers.ofstring()) .whencomplete((resp,t) -> { if (t != null ){ t.printstacktrace(); } else { system.out.println(resp.version()); system.out.println(resp.statuscode()); } }).join(); } |
执行之后可以看到返回的response的version为http_2
websocket
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
@test public void testwebsocket() throws interruptedexception { httpclient client = httpclient.newhttpclient(); websocket websocket = client.newwebsocketbuilder() .buildasync(uri.create( "ws://localhost:8080/echo" ), new websocket.listener() {
@override public completionstage<?> ontext(websocket websocket, charsequence data, boolean last) { // request one more websocket.request( 1 );
// print the message when it's available return completablefuture.completedfuture(data) .thenaccept(system.out::println); } }).join(); websocket.sendtext( "hello " , false ); websocket.sendtext( "world " , true );
timeunit.seconds.sleep( 10 ); websocket.sendclose(websocket.normal_closure, "ok" ).join(); } |
reactive streams
httpclient本身就是reactive的,支持reactive streams,这里举responsesubscribers.bytearraysubscriber的源码看看:
java.net.http/jdk/internal/net/http/responsesubscribers.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
public static class bytearraysubscriber<t> implements bodysubscriber<t> { private final function< byte [], t> finisher; private final completablefuture<t> result = new minimalfuture<>(); private final list<bytebuffer> received = new arraylist<>();
private volatile flow.subscription subscription;
public bytearraysubscriber(function< byte [],t> finisher) { this .finisher = finisher; }
@override public void onsubscribe(flow.subscription subscription) { if ( this .subscription != null ) { subscription.cancel(); return ; } this .subscription = subscription; // we can handle whatever you've got subscription.request( long .max_value); }
@override public void onnext(list<bytebuffer> items) { // incoming buffers are allocated by http client internally, // and won't be used anywhere except this place. // so it's free simply to store them for further processing. assert utils.hasremaining(items); received.addall(items); }
@override public void onerror(throwable throwable) { received.clear(); result.completeexceptionally(throwable); }
static private byte [] join(list<bytebuffer> bytes) { int size = utils.remaining(bytes, integer.max_value); byte [] res = new byte [size]; int from = 0 ; for (bytebuffer b : bytes) { int l = b.remaining(); b.get(res, from, l); from += l; } return res; }
@override public void oncomplete() { try { result.complete(finisher.apply(join(received))); received.clear(); } catch (illegalargumentexception e) { result.completeexceptionally(e); } }
@override public completionstage<t> getbody() { return result; } } |
小结
httpclient在java11从incubator变为正式版,相对于传统的httpurlconnection其提升可不是一点半点,不仅支持异步,也支持reactive streams,同时也支持了http2以及websocket,非常值得大家使用。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
原文链接:https://segmentfault.com/a/1190000016555671
查看更多关于Java11新特性之HttpClient小试牛刀的详细内容...