Home | 簡體中文 | 繁體中文 | 雜文 | 知乎專欄 | Github | OSChina 博客 | 雲社區 | 雲棲社區 | Facebook | Linkedin | 視頻教程 | 打賞(Donations) | About
知乎專欄多維度架構 | 微信號 netkiller-ebook | QQ群:128659835 請註明“讀者”

24.3. Apache HttpClient

24.3.1. Maven

			
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>cn.netkiller</groupId>
	<artifactId>example</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<dependencies>
		<!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.6</version>
		</dependency>

	</dependencies>
	<build>
		<sourceDirectory>src</sourceDirectory>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.5.1</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>			
			
		

24.3.2. HTTP POST 操作

24.3.2.1. Post Data

				
package cn.netkiller.httpclient;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class HttpClientPost {

	public static void main(String[] args) throws ClientProtocolException, IOException, NoSuchAlgorithmException {
		// TODO Auto-generated method stub

		String url = "http://api.netkiller.cn:8080/api/comment/list.jspx";
		String appId = "3175755150424665";
		String appKey = "yEjnjoSEOQpOP49od1IexLkyVB4HTi9c";
		String contentId = "1103";

		DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

		String date = dateFormat.format(new Date());

		String token = DatatypeConverter.printHexBinary(MessageDigest.getInstance("MD5").digest(String.format("%s&%s", appKey, date).getBytes("UTF-8"))).toLowerCase();

		CloseableHttpClient httpclient = HttpClients.createDefault();

		// appId=3175755150424665&contentId=1103&pageSize=100&timeStamp=2017-08-03
		// 10:20:00&token=e1180c0306aff7792c3e25699900dd0d
		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("appId", appId));
		params.add(new BasicNameValuePair("contentId", contentId));
		params.add(new BasicNameValuePair("pageSize", "10"));
		params.add(new BasicNameValuePair("timeStamp", date));
		params.add(new BasicNameValuePair("token", token));

		System.out.println(params.toString());
		UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params, Consts.UTF_8);
		System.out.println(urlEncodedFormEntity.toString());

		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(urlEncodedFormEntity);

		CloseableHttpResponse response = httpclient.execute(httpPost);

		System.out.println(response.getStatusLine());
		HttpEntity entity = response.getEntity();
		String responseBody = EntityUtils.toString(entity, "UTF-8");
		System.out.println(responseBody.toString());
		response.close();
	}

}
				
				
			

24.3.2.2. POST RAW 數據

				
import java.io.IOException;

import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

@SuppressWarnings("deprecation")
public class HTTPREST {

	public static void main(String[] args) throws ClientProtocolException, IOException {
		HttpClient httpClient = new DefaultHttpClient();

		try {
			HttpPost request = new HttpPost("http://test:123456@api.netkiller.cn/v1/test/create.json");
			StringEntity params = new StringEntity("{\"name\":\"neo\", \"nickname\":\"netkiller\"}", "UTF-8");
			request.addHeader("content-type", "application/json");
			request.addHeader("Accept", "application/json");
			request.setEntity(params);
			HttpResponse response = httpClient.execute(request);
			int statusCode = response.getStatusLine().getStatusCode();
			System.out.println(statusCode);

			if (response != null) {

				String responseBody = EntityUtils.toString(response.getEntity(), "UTF-8");
				System.out.println(responseBody.toString());
			}

		} catch (Exception ex) {
			ex.printStackTrace();
		} finally {
			httpClient.getConnectionManager().shutdown();
		}

	}

}
				
			

24.3.2.3. POST GBK 編碼得數據

有些國內短信運營商發送短信的介面只能接收 GBK 編碼,下面就是一個POST GBK編碼數據的範例。

				
package api.test.sms;

import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

public class SMS {

	public SMS() {
		// TODO Auto-generated constructor stub
	}

	public void sendSMS(String mobile, String message) throws ClientProtocolException, IOException {

		String url = "http://api.netkiller.cn/sms/v2/send";

		SimpleDateFormat sdf = new SimpleDateFormat("MMddHHmmss");
		String timestamp = sdf.format(Calendar.getInstance().getTime());

		CloseableHttpClient httpclient = HttpClients.createDefault();

		List<NameValuePair> params = new ArrayList<NameValuePair>();
		params.add(new BasicNameValuePair("apikey", "2863300994052170feb"));
		params.add(new BasicNameValuePair("mobile", mobile));
		params.add(new BasicNameValuePair("content", message));

		UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(params, "GBK");
		System.out.println(urlEncodedFormEntity.toString());

		HttpPost httpPost = new HttpPost(url);
		httpPost.setEntity(urlEncodedFormEntity);

		CloseableHttpResponse response = httpclient.execute(httpPost);

		HttpEntity entity = response.getEntity();
		String responseBody = EntityUtils.toString(entity, "UTF-8");

		System.out.println(params.toString());
		System.out.println(response.getStatusLine());
		System.out.println(responseBody.toString());
		response.close();

	}

	public static void main(String[] args) throws ClientProtocolException, IOException {
		// TODO Auto-generated method stub
		SMS sms = new SMS();
		String message = String.format("您的驗證碼是%s,在%s分鐘內輸入有效。如非本人操作請忽略此短信。", 8888, 10);
		sms.sendSMS("13113668800", message);
	}

}

				
			

24.3.3. HTTPS

24.3.3.1. Get https 介面

環境 Nginx SSL(openssl自頒發),nginx 通過proxy_pass連接 Tomcat

下面是 nginx 配置

			
server {
	listen 443 ssl spdy;
	server_name api.netkiller.cn;

	ssl_certificate /etc/nginx/ssl/api.netkiller.cn.crt;
	ssl_certificate_key /etc/nginx/ssl/api.netkiller.cn.key;
	ssl_session_cache shared:SSL:20m;
	ssl_session_timeout 60m;
	ssl_protocols TLSv1 TLSv1.1 TLSv1.2;

	charset utf-8;
	access_log /var/log/nginx/api.netkiller.cn.access.log;
	error_log /var/log/nginx/api.netkiller.cn.error.log;

	location / {
		proxy_pass http://127.0.0.1:7000;
		proxy_http_version 1.1;
		proxy_set_header X-Forwarded-For
		$proxy_add_x_forwarded_for;
	}

}
			
			

下面是 Java 程序

				
package cn.netkiller.example;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import org.apache.http.HttpEntity;
import org.apache.http.ParseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

public class NginxAndOpenSSLAndTomcatAndHttpclient {
	public static void main(String[] args) throws ParseException, IOException, NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
		SSLContextBuilder builder = new SSLContextBuilder();
		builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
		SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(builder.build());
		CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslFactory).build();

		HttpGet httpGet = new HttpGet("https://neo:netkiller@api.netkiller.cn/v1/news/today.json");
		CloseableHttpResponse response = httpclient.execute(httpGet);
		try {
			System.out.println(response.getStatusLine());
			HttpEntity entity = response.getEntity();
			String responseBody = EntityUtils.toString(entity, "UTF-8");
			System.out.println(responseBody.toString());
		} finally {
			response.close();
		}
	}
}
				
			

如果遇到配置問題,可以看一下 《Netkiller Linux Web 手札》

24.3.3.2. POST json 數據

			 	
package cn.netkiller.example;

import java.io.IOException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;

public class HttpClientSSLPost {

	public HttpClientSSLPost() {
		// TODO Auto-generated constructor stub
	}

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		SSLContextBuilder builder = new SSLContextBuilder();
		try {
			builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());

			SSLConnectionSocketFactory sslFactory = new SSLConnectionSocketFactory(builder.build());
			CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslFactory).build();

			HttpPost httpPost = new HttpPost("https://neo:YruuUCNXKe@api.netkiller.cn/v1/member/create.json");
			httpPost.addHeader("content-type", "application/json");
			httpPost.addHeader("Accept", "application/json");

			HttpEntity httpEntity = new StringEntity("{\"name\":\"neo\", \"nickname\":\"netkiler\",\"age\":\"18\"}", "UTF-8");
			
			httpPost.setEntity(httpEntity);

			CloseableHttpResponse response = httpclient.execute(httpPost);

			System.out.println(response.getStatusLine());
			HttpEntity entity = response.getEntity();
			String responseBody = EntityUtils.toString(entity, "UTF-8");
			System.out.println(responseBody.toString());
			response.close();
		} catch (NoSuchAlgorithmException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyStoreException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (KeyManagementException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		finally {

		}
	}

}
			 	
			

24.3.4. HTTP/2

HTTP/2實例

		
    @Test
    public void testHttp2() throws URISyntaxException, IOException, InterruptedException {
        HttpClient.newBuilder()
                .followRedirects(HttpClient.Redirect.SECURE)
                .version(HttpClient.Version.HTTP_2)
                .build()
                .sendAsync(HttpRequest.newBuilder()
                                .uri(new URI("https://http2.akamai.com/demo"))
                                .GET()
                                .build(),
                        HttpResponse.BodyHandler.asString())
                .whenComplete((resp,t) -> {
                    if(t != null){
                        t.printStackTrace();
                    }else{
                        System.out.println(resp.body());
                        System.out.println(resp.statusCode());
                    }
                }).join();
    }
		
		

24.3.5. Java11

24.3.5.1. sync get

			
	/**
     * --add-modules jdk.incubator.httpclient
     * @throws IOException
     * @throws InterruptedException
     * @throws URISyntaxException
     */
    @Test
    public void testGet() throws IOException, InterruptedException, URISyntaxException {
        HttpClient httpClient = HttpClient.newHttpClient();
        HttpRequest httpRequest = HttpRequest.newBuilder()
                .uri(new URI("https://www.baidu.com"))
                .header("User-Agent", "jdk 9 http client")
                .GET()
                .build();
        HttpResponse<String> httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandler.asString());
        System.out.println(httpResponse.statusCode());
        System.out.println(httpResponse.body());
    }
			
			

24.3.5.2. async get

			
    @Test
    public void testAsyncGet() throws URISyntaxException, InterruptedException, ExecutionException {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("https://www.baidu.com"))
                .GET()
                .build();

        CompletableFuture<HttpResponse<String>> response = client.sendAsync(request, HttpResponse.BodyHandler.asString());

        response.whenComplete((resp,t) -> {
            if(t != null){
                t.printStackTrace();
            }else{
                System.out.println(resp.body());
                System.out.println(resp.statusCode());
            }
        }).join();
    }
			
			

24.3.5.3. post form

post form

			
	@Test
    public void testPostForm() throws URISyntaxException {
        HttpClient client = HttpClient.newHttpClient();

        HttpRequest request = HttpRequest.newBuilder()
                .uri(new URI("http://www.w3school.com.cn/demo/demo_form.asp"))
                .header("Content-Type","application/x-www-form-urlencoded")
                .POST(HttpRequest.BodyProcessor.fromString("name1=value1&name2=value2"))
                .build();
        client.sendAsync(request, HttpResponse.BodyHandler.asString())
                .whenComplete((resp,t) -> {
                    if(t != null){
                        t.printStackTrace();
                    }else{
                        System.out.println(resp.body());
                        System.out.println(resp.statusCode());
                    }
        }).join();
    }
			
			

24.3.6. Host name 'api.netkiller.cn' does not match the certificate subject provided

這個問題是CN不匹配造成的,日誌如下

		
Exception in thread "main" javax.net.ssl.SSLPeerUnverifiedException: Host name 'api.netkiller.cn' does not match the certificate subject provided by the peer (EMAILADDRESS=netkiller@msn.com, CN=netkiller.cn, OU=CF, O=CF, L=Shenzhen, ST=Guangdong, C=CN)
		
		

解決方案一:重新做證書

		
Country Name (2 letter code) [XX]:CN
State or Province Name (full name) []:Guangdong
Locality Name (eg, city) [Default City]:Shenzhen
Organization Name (eg, company) [Default Company Ltd]:CF
Organizational Unit Name (eg, section) []:CF
Common Name (eg, your name or your server's hostname) []:api.netkiller.cn
Email Address []:netkiller@msn.com
			
		

解決方案二:是用CN上的域名連結

24.3.7. HttpStatus

		
package cn.netkiller.consul.controller;

import org.apache.http.HttpStatus;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class TestController {

	public TestController() {
		// TODO Auto-generated constructor stub
	}

	@RequestMapping("/health")
	public int health() {
		return HttpStatus.SC_OK;
	}
}