一,概述
Spring RestTemplate 调用REST API 给我们的开发工作带来了极大的方便, 默认的SimpleClientHttpRequestFactory 并不支持https的调用,我们可以通过引入Apache HttpClient实现对https的调用支持。
二,SpringBoot工程源码
1. 配置类
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import okhttp3.ConnectionPool;
import okhttp3.OkHttpClient;
/**
*
* 注册 RestTemplate
*
* @author 00fly
* @version [版本号, 2018年11月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Configuration
public class RestTemplateConfig
{
@Bean
RestTemplate restTemplate()
{
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
RestTemplate restTemplate = new RestTemplate(requestFactory);
// 注入HttpComponentsClientRestfulHttpRequestFactory
restTemplate.setRequestFactory(new HttpComponentsClientRestfulHttpRequestFactory());
// 设置编码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
/**
* Apache HttpClient
*
* @return
* @see [类、类#方法、类#成员]
*/
private HttpClient httpClient()
{
// 支持HTTP、HTTPS
Registry<ConnectionSocketFactory> registry =
RegistryBuilder.<ConnectionSocketFactory> create().register("http", PlainConnectionSocketFactory.getSocketFactory()).register("https", SSLConnectionSocketFactory.getSocketFactory()).build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
connectionManager.setValidateAfterInactivity(2000);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(65000) // 服务器返回数据(response)的时间,超时抛出read timeout
.setConnectTimeout(5000) // 连接上服务器(握手成功)的时间,超时抛出connect timeout
.setConnectionRequestTimeout(1000)// 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
}
/**
* OkHttpClient实现
*
* @return
* @see [类、类#方法、类#成员]
*/
// @Bean
public RestTemplate okHttpRestTemplate()
{
ClientHttpRequestFactory requestFactory = new OkHttp3ClientHttpRequestFactory(okHttpClient());
RestTemplate restTemplate = new RestTemplate(requestFactory);
// RestTemplate设置编码
restTemplate.getMessageConverters().set(1, new StringHttpMessageConverter(StandardCharsets.UTF_8));
return restTemplate;
}
/**
* OkHttpClient
*
* @return
* @see [类、类#方法、类#成员]
*/
private OkHttpClient okHttpClient()
{
// 设置连接池参数,最大空闲连接数200,空闲连接存活时间10s
ConnectionPool connectionPool = new ConnectionPool(200, 10, TimeUnit.SECONDS);
OkHttpClient okHttpClient = new OkHttpClient.Builder().retryOnConnectionFailure(false).connectionPool(connectionPool).connectTimeout(3, TimeUnit.SECONDS).readTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).build();
return okHttpClient;
}
}
2. 测试代码
package com.fly.rest;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import javax.imageio.ImageIO;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.SystemUtils;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import com.fly.HttpBootApplication;
import com.fly.core.utils.JsonBeanUtils;
import com.fly.rest.bean.SearchReq;
import lombok.extern.slf4j.Slf4j;
/**
*
* RestTemplateTest
*
* @author 00fly
* @version [版本号, 2018年11月6日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Slf4j
@SpringBootTest(classes = HttpBootApplication.class)
public class RestTemplateTest
{
@Autowired
private RestTemplate restTemplate;
@Test
public void testDownLoadImg001()
throws IOException
{
String downUrl = "https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg";
ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(downUrl, byte[].class);
byte[] body = responseEntity.getBody();
// 数据落地
File dest = new File("upload/img_" + System.currentTimeMillis() + ".jpg");
dest.getParentFile().mkdirs();
try (FileOutputStream fos = new FileOutputStream(dest))
{
fos.write(body);
}
if (SystemUtils.IS_OS_WINDOWS)
{
Runtime.getRuntime().exec("cmd /c start " + dest.getParentFile().getCanonicalPath());
}
}
@Test
public void testDownLoadImg002()
throws IOException
{
String downUrl = "https://00fly.online/upload/2019/02/201902262129360274AKuFZcUfip.jpg";
ResponseEntity<byte[]> responseEntity = restTemplate.getForEntity(downUrl, byte[].class);
byte[] body = responseEntity.getBody();
File dest = new File("upload/img_" + System.currentTimeMillis() + ".jpg");
dest.getParentFile().mkdirs();
// 保存到本地
BufferedImage image = ImageIO.read(new ByteArrayInputStream(body));
ImageIO.write(image, "jpg", dest);
if (SystemUtils.IS_OS_WINDOWS)
{
Runtime.getRuntime().exec("cmd /c start " + dest.getParentFile().getCanonicalPath());
}
}
@Test
public void testExchange001()
{
String url = "https://www.so.com/s";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
headers.add("token", "Bearer");
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
params.add("type", 1);
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity;
// GET with headers
responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity);
// POST
responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testExchange002()
{
/**** url支持占位符 ****/
String url = "http://localhost:8080/rest/user/list/{page}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("username", "用户1");
params.add("password", "123456");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity;
// GET、POST
responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, String.class, 1);
log.info("ResponseEntity={}", responseEntity);
Map<String, Integer> vars = Collections.singletonMap("page", 1);
responseEntity = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class, vars);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testFormData001()
{
// POST请求只能用MultiValueMap
String url = "https://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("q1", "java");
params.add("q2", "python");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
if (RandomUtils.nextBoolean())
{
String response = restTemplate.postForObject(url, requestEntity, String.class);
log.info("ResponseBody={}", response);
}
else
{
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity.getBody());
}
}
@Test
public void testFormData002()
{
/**** url支持占位符 ****/
String url = "http://localhost:8080/rest/user/list/{page}";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("username", "user001");
params.add("password", "123456");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity;
String response = restTemplate.postForObject(url, requestEntity, String.class, 1);
log.info("ResponseBody={}", response);
responseEntity = restTemplate.postForEntity(url, requestEntity, String.class, 1);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testGet001()
{
/**** url支持占位符 ****/
String url = "http://localhost:8080/rest/user/list/{page}";
String response = restTemplate.getForObject(url, String.class, 1);
log.info("ResponseBody={}", response);
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class, 1);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testGet002()
{
String url = "https://httpbin.org/get";
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
log.info("ResponseEntity StatusCode={}", responseEntity.getStatusCode());
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testGet003()
{
String url = "https://httpbin.org/get";
String responseBody = restTemplate.getForObject(url, String.class);
log.info("responseBody={}", responseBody);
}
/**
* RestTemplateConfig需引入HttpComponentsClientRestfulHttpRequestFactory支持GET请求携带body数据
*
* @throws IOException
*/
@Test
public void testGetWithBody()
throws IOException
{
String url = "http://127.0.0.1:9380/v1/tracking/component/output/data/download";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
// 必须用HashMap?
Map<String, String> params = new HashMap<>();
params.put("job_id", "");
params.put("role", "guest");
params.put("party_id", "10000");
params.put("component_name", "intersect_0");
HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(params, headers);
// 支持GET请求携带body数据
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(url, HttpMethod.GET, requestEntity, byte[].class);
byte[] bytes = responseEntity.getBody();
File dest = new File("download/dest.tar.gz");
dest.getParentFile().mkdirs();
try (OutputStream fos = new FileOutputStream(dest))
{
fos.write(bytes);
}
}
/**
* 演示@RequestBody请求
*
* @throws IOException
*/
@Test
public void testJsonRequestBody()
throws IOException
{
String url = "https://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
Map<String, String> map = new HashMap<>();
ResponseEntity<String> responseEntity;
int num = RandomUtils.nextInt(1, 4);
switch (num)
{
case 1: // 方式1,javaBean
SearchReq req = new SearchReq();
req.setPageNo(1);
req.setPageSize(5);
req.setKeyword("0");
responseEntity = restTemplate.postForEntity(url, new HttpEntity<>(req, headers), String.class);
log.info("ResponseEntity = {}", responseEntity);
break;
case 2: // 方式2,HashMap
map.clear();
map.put("pageNo", "2");
map.put("pageSize", "10");
map.put("keyword", "1");
responseEntity = restTemplate.postForEntity(url, new HttpEntity<>(map, headers), String.class);
log.info("ResponseEntity={}", responseEntity);
break;
case 3: // 方式3,Json字符串
map.clear();
map.put("pageNo", "3");
map.put("pageSize", "15");
map.put("keyword", "2");
responseEntity = restTemplate.postForEntity(url, new HttpEntity<>(JsonBeanUtils.beanToJson(map, false), headers), String.class);
log.info("ResponseEntity={}", responseEntity);
break;
}
}
@Test
public void testPost001()
{
String url = "https://www.so.com/";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
String responseBody = restTemplate.postForObject(url, requestEntity, String.class);
log.info("responseBody={}", responseBody);
}
@Test
public void testPost002()
{
// POST请求只能用MultiValueMap
String url = "http://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
params.add("id", "123456");
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testUpload001()
throws IOException
{
// POST请求只能用MultiValueMap
String url = "https://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
// 注意:FileSystemResource只适用于文件,Jar內运行报错
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
String path = new ClassPathResource("123.jpg").getURL().getPath();
log.info(path);
FileSystemResource file = new FileSystemResource(path);
// 多次调用,后台接受到的是MultipartFile[]
params.add("file", file);
params.add("file", file);
params.add("file", file);
params.add("name", "girl");
params.add("age", "18");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testUpload002()
{
// POST请求只能用MultiValueMap
String url = "https://httpbin.org/post";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.MULTIPART_FORM_DATA);
headers.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
// Resource适用于文件、Jar內
MultiValueMap<String, Object> params = new LinkedMultiValueMap<>();
Resource file = new ClassPathResource("123.jpg");
// 多次调用,后台接受到的是MultipartFile[]
params.add("file", file);
params.add("file", file);
params.add("file", file);
params.add("name", "girl");
params.add("age", "18");
HttpEntity<MultiValueMap<String, Object>> requestEntity = new HttpEntity<>(params, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
log.info("ResponseEntity={}", responseEntity);
}
@Test
public void testWebService()
throws IOException
{
String url = "http://127.0.0.1:7879/demo/services/ws_inputService";
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.TEXT_XML);
headers.setAccept(Arrays.asList(MediaType.TEXT_XML));
InputStream is = this.getClass().getResourceAsStream("/request");
String requestString = IOUtils.toString(is, StandardCharsets.UTF_8);
HttpEntity<String> requestEntity = new HttpEntity<>(requestString, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, requestEntity, String.class);
log.info("ResponseEntity Body = {}", responseEntity.getBody());
}
}
三,springmvc工程代码(备查)
1,首先注册
package com.fly.config;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
/**
*
* 注册 RestTemplate
*
* @author 00fly
* @version [版本号, 2018年11月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@Configuration
public class RestTemplateConfig
{
@Bean
public RestTemplate restTemplate()
{
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient());
return new RestTemplate(requestFactory);
}
/**
* Apache HttpClient
*
* @return
* @see [类、类#方法、类#成员]
*/
private HttpClient httpClient()
{
// 支持HTTP、HTTPS
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory> create()
.register("http", PlainConnectionSocketFactory.getSocketFactory())
.register("https", SSLConnectionSocketFactory.getSocketFactory())
.build();
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(200);
connectionManager.setDefaultMaxPerRoute(100);
connectionManager.setValidateAfterInactivity(2000);
RequestConfig requestConfig = RequestConfig.custom()
.setSocketTimeout(65000) // 服务器返回数据(response)的时间,超时抛出read timeout
.setConnectTimeout(5000) // 连接上服务器(握手成功)的时间,超时抛出connect timeout
.setConnectionRequestTimeout(1000)// 从连接池中获取连接的超时时间,超时抛出ConnectionPoolTimeoutException
.build();
return HttpClientBuilder.create().setDefaultRequestConfig(requestConfig).setConnectionManager(connectionManager).build();
}
}
2,Spring 配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">
<!-- 使用默认扫描方式, 排除controller类注解 -->
<context:component-scan base-package="com.fly" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
</beans>
3,单元测试代码
package com.fly.rest;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
/**
*
* RestTest
*
* @author 00fly
* @version [版本号, 2018年11月20日]
* @see [相关类/方法]
* @since [产品/模块版本]
*/
@RunWith(SpringRunner.class)
@ContextConfiguration({"/applicationContext.xml"})
public class RestTest
{
private static final Logger LOGGER = LoggerFactory.getLogger(RestTest.class);
@Autowired
private RestTemplate restTemplate;
@Test
public void testHttps()
{
String url = "https://www.baidu.com/"; // 百度返回乱码
url = "https://www.so.com/";
String responseBody;
responseBody = restTemplate.getForObject(url, String.class);
LOGGER.info("responseBody={}", responseBody);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
responseBody = restTemplate.postForObject(url, requestEntity, String.class);
LOGGER.info("responseBody={}", responseBody);
}
}
4,运行结果
2018-11-22 10:47:05 |INFO |main|Loaded default TestExecutionListener class names from location [META-INF/spring.factories]: [org.springframework.test.context.we
b.ServletTestExecutionListener, org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener, org.springframework.test.context.suppor
t.DependencyInjectionTestExecutionListener, org.springframework.test.context.support.DirtiesContextTestExecutionListener, org.springframework.test.context.trans
action.TransactionalTestExecutionListener, org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]|
2018-11-22 10:47:05 |INFO |main|Using TestExecutionListeners: [org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener@13c78c0b,
org.springframework.test.context.support.DependencyInjectionTestExecutionListener@12843fce, org.springframework.test.context.support.DirtiesContextTestExecutio
nListener@3dd3bcd]|
2018-11-22 10:47:05 |INFO |main|Loading XML bean definitions from class path resource [applicationContext.xml]|
2018-11-22 10:47:06 |INFO |main|Refreshing org.springframework.context.support.GenericApplicationContext@6a41eaa2: startup date [Thu Nov 22 10:47:06 CST 2018];
root of context hierarchy|
2018-11-22 10:47:09 |INFO |main|responseBody=<!DOCTYPE html>
<!--[if lt IE 7 ]><html class="ie6"><![endif]-->
<!--[if IE 7 ]><html class="ie7"><![endif]-->
<!--[if IE 8 ]><html class="ie8"><![endif]-->
<!--[if IE 9 ]><html class="ie9"><![endif]-->
<!--[if (gt IE 9)|!(IE)]><!--><html class="w3c"><!--<![endif]-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>360搜索,SO靠谱</title>
<link rel="dns-prefetch" href="//p.ssl.qhimg.com"><link rel="dns-prefetch" href="//s.ssl.qhimg.com"><link rel="dns-prefetch" href="//s.ssl.qhres.com">
<link rel="dns-prefetch" href="//p418.ssl.qhimgs4.com"><link rel="dns-prefetch" href="//p419.ssl.qhimgs4.com"><link rel="dns-prefetch" href="//p420.ssl.qhimgs4.
com">
<link rel="search" type="application/opensearchdescription+xml" href="https://www.so.com/soopensearch.xml" title="360搜索">
<meta name="keywords" content="360搜索,360搜索,网页搜索,视频搜索,图片搜索,音乐搜索,新闻搜索,软件搜索,学术搜索">
<meta name="description" content="360搜索是安全、精准、可信赖的新一代搜索引擎,依托于360母品牌的安全优势,全面拦截各类钓鱼欺诈等恶意网站,提供更放心的搜索服务。 360搜索 so靠谱。">
<meta content="always" name="referrer">
<noscript>
<img src="//s.qhupdate.com/so/click.gif?pro=so&pid=home&mod=noscript&t=1542854819" style="display:none">
<meta http-equiv="refresh" content="0; url=http://www.so.com/haosou.html?src=home">
</noscript>
<link rel="shortcut icon" href="https://s.ssl.qhres.com/static/52166db8c450f68d.ico" type="image/x-icon">
<script>var TIME = {_: +new Date}</script><script>(function(e,t){function n(e){return t.getElementById(e)}function r(){u("stc_nls",1,1)}function i(n,r){var i=""
;try{i=p[n]||"",i.length<99&&(u(r,0),t.documentElement.style.display="none",l(),e.onbeforeunload=null,location.reload(!0))}catch(s){l()}return i}function s(e,t)
{try{p[e]=t,t!==p[e]&&l()}catch(n){l()}}function o(e){var n=t.cookie.split("; ");for(var r=0,i=n.length,s;r<i;r++){s=n[r].split("=");if(s[0]===e)return s[1]}ret
......