基本概念
官网 https://square.github.io/okhttp/
github地址 https://github.com/square/okhttp
okhttp4.x和okhttp3.x;4.x使用kotlin来编写的
Request
Request类封装了请求报文信息:请求的Url地址、请求的方法(如GET、POST等)、各种请求头(如Content-Type、Cookie)以及可选的请求体。一般通过内部类Request.Builder的链式调用生成Request对象。
Call
Call代表了一个实际的HTTP请求,它是连接Request和Response的桥梁,通过Request对象的newCall()方法可以得到一个Call对象。Call对象既支持同步获取数据,也可以异步获取数据。
执行Call对象的execute()方法,会阻塞当前线程去获取数据,该方法返回一个Response对象。
执行Call对象的enqueue()方法,不会阻塞当前线程,该方法接收一个Callback对象,当异步获取到数据之后,会回调执行Callback对象的相应方法。如果请求成功,则执行Callback对象的onResponse方法,并将Response对象传入该方法中;如果请求失败,则执行Callback对象的onFailure方法。
Response
Response类封装了响应报文信息:状态吗(200、404等)、响应头(Content-Type、Server等)以及可选的响应体。可以通过Call对象的execute()方法获得Response对象,异步回调执行Callback对象的onResponse方法时也可以获取Response对象。
应用场景
使用OkHttp执行get请求和带参数的get请求;
使用OkHttp执行post请求、带参数的post请求;
支持contentType为none、application/json、form-data、application/x-www-form-urlencoded
图从postman截取
代码实现如下:
getUrl 方法: 用于拼接url;post或者get请求都可能会需要拼接url;
Okhttp拼接url功能不错,没有?会自动添加?;有?会直接在后面添加参数。
curl -X GET "http://localhost:9099/requestDemo/getDemo?name=111&strDemo=222" -H "accept: */*"
getBody方法:用于拼接post请求的body内容;以下是OkHttp的几种body和对应关系
@Slf4j
public class OkhttpUtils {
private static OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(6, TimeUnit.SECONDS).build();
private static final MediaType JSON = MediaType.parse("application/json");
private static final String MEDIATYPE_NONE = "none";
private static final String MEDIATYPE_JSON = "application/json";
private static final String MEDIATYPE_FORM = "form-data";
private static final String MEDIATYPE_FORM_URLENCODED = "application/x-www-form-urlencoded";
/**
* @Author JackZhou
* @Description 执行get请求
**/
public static String execRequest(String url, Map<String, String> headers){
Request.Builder requestBuilder = new Request.Builder().url(url);
if(headers != null && headers.size() >0 ){
headers.entrySet().stream().forEach(entry -> requestBuilder.header(entry.getKey(), entry.getValue()));
}
Request request = requestBuilder.url(url).build();
try {
Response response = okHttpClient.newCall(request).execute();
return response.body().string();
} catch (IOException e) {
log.info("执行http请求出错,地址:{}", url, e);
return null;
}
}
/**
* @Author JackZhou
* @Description 执行post请求
**/
public static String execPostRequest(String url, Map<String, String> headers, RequestBody requestBody){
Request.Builder requestBuilder = new Request.Builder().url(url);
if(headers != null && headers.size() >0 ){
headers.entrySet().stream().forEach(entry -> requestBuilder.header(entry.getKey(), entry.getValue()));
}
Request request = requestBuilder.url(url).post(requestBody).build();
try {
Response response = okHttpClient.newCall(request).execute();
return response.body().string();
} catch (IOException e) {
log.info("执行http请求出错,地址:{}", url, e);
return null;
}
}
/**
* @Author JackZhou
* @Description 得到post请求的RequestBody
**/
public static RequestBody getBody(String type, Map<String, String> formParam, String body){
switch (type) {
case MEDIATYPE_JSON:
if(StringUtils.isEmpty(body)){
RequestBody.create(null, "");
}
return RequestBody.create(JSON, body);
case MEDIATYPE_FORM:
MultipartBody.Builder formBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
if(formParam != null && formParam.size() >0 ){
formParam.entrySet().stream().forEach(entry -> formBuilder.addFormDataPart(entry.getKey(), entry.getValue()));
}
return formBuilder.build();
case MEDIATYPE_FORM_URLENCODED:
FormBody.Builder builder = new FormBody.Builder();
if(formParam != null && formParam.size() >0 ){
formParam.entrySet().stream().forEach(entry -> builder.add(entry.getKey(), entry.getValue()));
}
return builder.build();
case MEDIATYPE_NONE:
return RequestBody.create(null, "");
default:
throw new IllegalArgumentException("不支持的mediaType:" + type);
}
}
/**
* @Author JackZhou
* @Description 得到拼接后的url @RequestParam参数
**/
public static String getUrl(String url, Map<String, String> params){
HttpUrl.Builder builder = HttpUrl.parse(url).newBuilder();
if(params != null && params.size() > 0){
params.entrySet().stream().forEach(entry -> builder.addQueryParameter(entry.getKey(), entry.getValue()));
}
return builder.build().toString();
}
测试验证
@Data
public class Person {
private String id;
private String name;
private int age;
}
@RestController
@Api(tags = "模拟不同的meidaType")
@RequestMapping(value = "/requestDemo")
public class MediaTypeController {
@ApiOperation("模拟普通get请求")
@RequestMapping(value = "/getDemo", method = RequestMethod.GET)
public String getDemo(@RequestParam(required = false) String name, @RequestParam(required = false) String strDemo){
return "success: " + name + ";strDemo:" + strDemo;
}
@ApiOperation("模拟普通post请求")
@RequestMapping(value = "/getPost", method = RequestMethod.POST)
public String postDemo(){
return "success";
}
@ApiOperation("模拟普通post 带参数请求")
@RequestMapping(value = "/getPostParam", method = RequestMethod.POST)
public String postParamDemo(@RequestParam(required = false) String name, @RequestParam(required = false) String strDemo){
return "success:" + name;
}
@ApiOperation("模拟post xxx-form请求")
@RequestMapping(value = "/getPostModel", method = RequestMethod.POST)
public String postModelDemo(@ModelAttribute Person person){
if(person != null){
return "success:" + person.getName();
}else{
return "success";
}
}
@ApiOperation("模拟普通post 传json请求")
@RequestMapping(value = "/getPostJson", method = RequestMethod.POST)
public String postJsonDemo(@RequestBody Person person){
if(person != null){
return "success:" + person.getName();
}else{
return "success";
}
}
@ApiOperation("模拟普通post form请求")
@RequestMapping(value = "/getPostForm", method = RequestMethod.POST)
public String postDemo(HttpServletRequest request){
System.out.println(request.getParameter("name"));
System.out.println(request.getParameter("strDemo"));
return "success";
}
}
偷懒,测试main方法;加到OkhttpUtils类中即可
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("name", "张三");
map.put("strDemo", "上山打老虎");
Map<String, String> pMap = new HashMap<>();
pMap.put("name", "张三");
pMap.put("id", "11111");
pMap.put("age", "111");
// 测试get请求、有参无参均可
// String getUrl = "http://localhost:9099/requestDemo/getDemo";
// log.info(execRequest(getUrl(getUrl, map), null));
// 测试基本的post url后面有参数或者没有
// String postUrl = "http://localhost:9099/requestDemo/getPost";
// //String postUrl = "http://localhost:9099/requestDemo/getPostParam";
// log.info(execPostRequest(postUrl, null, getBody(MEDIATYPE_NONE, null, null)));
// log.info(execPostRequest(execRequest(getUrl(getUrl, map), null, getBody(MEDIATYPE_NONE, null, null)));
// 测试post的application/x-www-form-urlencoded 对应@ModelAttribute注解
// String postUrl = "http://localhost:9099/requestDemo/getPostModel";
// //log.info(execPostRequest(postUrl, null, getBody(MEDIATYPE_FORM_URLENCODED, null, null)));
// log.info(execPostRequest(postUrl, null, getBody(MEDIATYPE_FORM_URLENCODED, pMap, null)));
// 测试post的application/json 对应@RequestBody注解
// String postUrl = "http://localhost:9099/requestDemo/getPostJson";
// log.info(execPostRequest(postUrl, null, getBody(MEDIATYPE_JSON, null, "{ \"age\": 11, \"id\": \"sdsb111\", \"name\": \"张三\"}")));
// 测试post的form-data
// String postUrl = "http://localhost:9099/requestDemo/getPostForm";
// log.info(execPostRequest(postUrl, null, getBody(MEDIATYPE_FORM, map, null)));
}
注意事项
1、如果post请求的内容为空,不能传空的requestBody会报错
java.lang.IllegalArgumentException: method POST must have a request body
### 添加以下代码
#如果post请求的body和contentType为空
RequestBody.create(null, "")
2、Response需要关闭
有些方法会关闭资源,有些不会,这一点要注意。
response.body().string(); //stirng里面会关闭资源
response.isSuccessful(); //如果判断是否 需要response.close()
3、推荐使用OkhttpClient单例
4、压缩支持
默认开启压缩支持,不需要手动设置(手动设置需要更多设置); 原理:使用拦截器,解析压缩数据
5、默认开启Keep-Alive;