使用OkHttp发送HTTP请求

技术公众号:Java In Mind(Java_In_Mind),欢迎关注!

OkHttp简介

OkHttp是一个用于Android、Kotlin、Java的HTTP客户端,性能高效,API封装友好,主要有如下特点:

  • 支持HTTP/2对同个服务端的请求共享一个Socket
  • 使用连接池可减少请求延迟(HTTP / 2不可用)
  • 使用GZIP缩小下载量
  • 缓存响应结果防止网络重复请求

使用OkHttp,在网络出现问题的时候,它会将常见的连接问题恢复;

如果服务端拥有多个IP,OkHttp会在请求失败的情况下尝试其他IP,这对于IPv4 + IPv6数据中心中托管的多活服务是很有必要的;

OkHttp支持TLS功能;

OkHttp的API设计对开发者友好,设计为链式风格的Builder,并且支持同步阻塞调用和异步回调调用。

OkHttp4

OkHttp4.x在2019年6月份正式发布,从Java语言变为Kotlin语言,使用了Kotlin的一些高效语法,并且保持和OkHttp3一样的功能,为了保持兼容性以便升级,保持了二进制兼容、Java源码兼容和Kotlin源码兼容,使用japicmp强制执行二进制兼容性,并且报名保持为okhttp3,升级在方法也提供了升级指导:https://square.github.io/okhttp/upgrading_to_okhttp_4/

这里使用OkHttp3来简单使用一下:


    com.squareup.okhttp3
    okhttp
    3.14.4

模拟服务

@RestController
@RequestMapping("/demo")
@SpringBootApplication
@Slf4j
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    private List foos = new CopyOnWriteArrayList<>();

    @GetMapping("/list")
    public ResponseEntity list(@RequestParam(value = "name", required = false) String name) {
        log.info("accept a list request...");
        boolean emptyQuery = StringUtils.isEmpty(name);
        return ResponseEntity
            .ok(foos.stream().filter(i -> emptyQuery || i.getName().equals(name)).collect(Collectors.toList()));
    }

    @PostMapping
    public ResponseEntity create(@RequestBody Foo foo) {
        log.info("accept create request,foo:{}", foo);
        // uuid
        foo.setId(UUID.randomUUID().toString());
        // add
        foos.add(foo);
        return ResponseEntity.ok(foo);
    }

    @GetMapping("/error")
    public ResponseEntity error() {
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body("error");
    }

    @Data
    @AllArgsConstructor
    public static class Foo {
        private String id;
        private String name;
        private int age;
    }
}

发起POST请求

OkHttpClient okHttpClient = new OkHttpClient();

// request body
Map foo = new HashMap<>();
foo.put("name", "HTTP");
foo.put("age", "18");

Request request = new Request.Builder().url("http://localhost:8080/demo")
    .post(RequestBody.create(MediaType.get("application/json"), JSONObject.toJSONString(foo))).build();

try (Response response = okHttpClient.newCall(request).execute()) {
    ResponseBody body = response.body();
    if (response.isSuccessful()) {
        log.info("success:{}", body == null ? "" : body.string());
    } else {
        log.error("error,statusCode={},body={}", response.code(), body == null ? "" : body.string());
    }
}

发起GET请求

OkHttpClient okHttpClient = new OkHttpClient();

Request request = new Request.Builder().url("http://localhost:8080/demo/list").build();
try (Response response = okHttpClient.newCall(request).execute()) {
    ResponseBody body = response.body();
    if (response.isSuccessful()) {
        log.info("success:{}", body == null ? "" : body.string());
    } else {
        log.error("error,statusCode={},body={}", response.code(), body == null ? "" : body.string());
    }
}

发起GET请求带查询参数

OkHttpClient okHttpClient = new OkHttpClient();

HttpUrl httpUrl = HttpUrl.parse("http://localhost:8080/demo/list").newBuilder()
.addQueryParameter("name", "HTTP").build();

Request request = new Request.Builder().url(httpUrl.toString()).build();
try (Response response = okHttpClient.newCall(request).execute()) {
    ResponseBody body = response.body();
    if (response.isSuccessful()) {
        log.info("success:{}", body == null ? "" : body.string());
    } else {
        log.error("error,statusCode={},body={}", response.code(), body == null ? "" : body.string());
    }
}

其他配置

  • 设置超时

    OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(Duration.ofSeconds(10))//连接超时
                    .writeTimeout(Duration.ofSeconds(5))//写超时,也就是请求超时
                    .readTimeout(Duration.ofSeconds(5))//读取超时
                    .callTimeout(Duration.ofSeconds(15))//调用超时,也是整个请求过程的超时
                    .build();
    
  • 使用拦截器设置全局Header

    //自定义拦截器,设置header
    public static class DefaultContentTypeInterceptor implements Interceptor {
    
        private String contentType;
        
        public DefaultContentTypeInterceptor(String contentType) {
            this.contentType = contentType;
        }
    
        public Response intercept(Interceptor.Chain chain)
                throws IOException {
    
            Request originalRequest = chain.request();
            Request requestWithUserAgent = originalRequest
                    .newBuilder()
                    .header("Content-Type", contentType)
                    .build();
    
            return chain.proceed(requestWithUserAgent);
        }
    }
    
    // 设置
    OkHttpClient httpClient = new OkHttpClient.Builder()
                    .addInterceptor(new DefaultContentTypeInterceptor("application/json"))
                    .build();
    
  • Basic 认证

    OkHttpClient okHttpClient = new OkHttpClient.Builder().authenticator(new Authenticator() {
        @Override
        public Request authenticate(Route route, Response response) throws IOException {
            if (response.request().header("Authorization") != null) {
                return null; 
            }
            String credential = Credentials.basic("username", "password");
            return response.request().newBuilder().header("Authorization", credential).build();
        }
    }).build();
    

总结

这里只做了简单的使用,OkHttp还有许多功能,请求缓存、异步回调等,OkHttp的API相对来说比较友好,可能也是我自己喜欢这种链式调用和Builder的模式,对于发起HTTP请求对Request的封装到Response的封装,结果的解析都比较简单,也支持用户自定义一些拦截做设置等。

你可能感兴趣的:(使用OkHttp发送HTTP请求)