搜索词>>httpclient 耗时0.0030
  • HttpClient 4.x 使用详解(基础篇)

    Apache HttpClient 4.x 使用详解<h2>1.httpclient获取请求响应的状态码</h2> 在发送Http请求之后 - 我们得到一个<em>org.apache.http.HttpResponse</em>的实例- 它允许我们访问响应的状态行,并隐含地显示状态码: <pre> <code class="language-java">response.getStatusLine().getStatusCode()</code></pre> 使用这个,我们可以<strong>验证我们从服务器收到的代码确实是正确的</strong>: <pre> <code class="language-java">@Test public void givenGetRequestExecuted_whenAnalyzingTheResponse_thenCorrectStatusCode() throws ClientProtocolException, IOException { HttpClient client = HttpClientBuilder.create().build(); HttpResponse response = client.execute(new HttpGet(SAMPLE_URL)); int statusCode = response.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK)); }</code></pre> <blockquote> <p>请注意,我们通过<em>org.apache.http.HttpStatus</em>使用库中<strong>预定义的状态码</strong>。</p> </blockquote> <h2>2.配置httpclient超时</h2> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.1通过原始<em>字符串</em>参数配置超时</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">该<em>HttpClient的</em>自带了大量的配置参数-所有这些都可以在一个通用的,地图状的方式来设置。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">有<strong>3个超时参数需要配置</strong>:</span></span></span></p> <pre> <code class="language-java">DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); httpParams.setParameter( CoreConnectionPNames.CONNECTION_TIMEOUT, timeout * 1000); httpParams.setParameter( CoreConnectionPNames.SO_TIMEOUT, timeout * 1000); // httpParams.setParameter( // ClientPNames.CONN_MANAGER_TIMEOUT, new Long(timeout * 1000));</code></pre> <blockquote> <p>注意:由于<a href="https://issues.apache.org/jira/browse/HTTPCLIENT-1418" rel="external nofollow" style="box-sizing:border-box; background-color:#ffffff; color:#63b175; text-decoration:none; font-family:raleway; font-size:18px; font-style:normal; font-variant-ligatures:normal; font-variant-caps:normal; font-weight:normal; letter-spacing:normal; orphans:2; text-align:start; text-transform:none; white-space:normal; widows:2; word-spacing:0px; -webkit-text-stroke-width:0px" target="_blank" title="当ClientPNames.CONN_MANAGER_TIMEOUT被设置时,类抛出异常">此JIRA</a>(4.3.2中的规定),最后一个参数 - 连接管理器超时 - 在使用4.3.0或4.3.1版本时被注释掉了。</p> </blockquote> <h3>2.2<strong>通过API配置超时</strong></h3> 这些参数中最重要的参数 - 前两个 - 也可以通过更安全的API来设置: <pre> <code class="language-java">DefaultHttpClient httpClient = new DefaultHttpClient(); int timeout = 5; // seconds HttpParams httpParams = httpClient.getParams(); HttpConnectionParams.setConnectionTimeout( httpParams, timeout * 1000); // http.connection.timeout HttpConnectionParams.setSoTimeout( httpParams, timeout * 1000); // http.socket.timeout</code></pre> 第三个参数在<em>HttpConnectionParams</em>中没有自定义设置器,它仍然需要通过<em>setParameter</em>方法手动<em>设置</em>。 <h3>2.3<strong>使用新的4.3配置超时。生成器</strong></h3> 4.3中介绍的流畅的构建器API提供<strong>了在较高级别设置超时的正确方法</strong>: <pre> <code class="language-java">int timeout = 5; RequestConfig config = RequestConfig.custom() .setConnectTimeout(timeout * 1000) .setConnectionRequestTimeout(timeout * 1000) .setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create().setDefaultRequestConfig(config).build();</code></pre> <h3>2.4<strong>超时属性说明</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在,我们来解释这些不同类型的超时的含义:</span></span></span></p> <ul style="list-style-type:disc"> <li><strong>Connection Timeout</strong> (<em>http.connection.timeout</em>) -建立与远程主机的连接的时间</li> <li><strong>Socket Timeout</strong> (<em>http.socket.timeout</em>) -的时间等待数据-建立连接之后; 两个数据包之间不活动的最长时间</li> <li><strong>Connection Manager Timeout</strong> (<strong>setConnectionRequestTimeout</strong>)(<em>http.connection-manager.timeout</em>) -connect Manager获取Connection 超时时间。单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。</li> </ul> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">前两个参数 - 连接和套接字超时 - 是最重要的,但为获得连接设置超时在高负载情况下显然非常重要,这就是为什么第三个参数不应该被忽略的原因。</span></span></span></p> <h3>2.5<strong>使用<em>HttpClient</em></strong></h3> 配置完成后,客户端不能用于执行HTTP请求: <pre> <code class="language-java">HttpGet getMethod = new HttpGet("http://host:8080/path"); HttpResponse response = httpClient.execute(getMethod); System.out.println( "HTTP Status of response: " + response.getStatusLine().getStatusCode());</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">使用先前定义的客户端,与主机的连接将<strong>在5秒内超时</strong>,如果连接已建立但未收到数据,则超时也将增加<strong>5秒</strong>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">请注意,连接超时将导致<strong><em>org.apache.http.conn.ConnectTimeoutException</em>被抛出</strong>,而套接字超时将导致<strong>一个<em>java.net.SocketTimeoutException</em></strong>。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">2.6</span></span></span><strong>硬超时</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">虽然在建立HTTP连接和不接收数据时设置超时非常有用,但有时我们需要<strong>为整个请求</strong>设置<strong>硬超时</strong>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">例如,下载一个潜在的大文件适合这个类别 - 在这种情况下,连接可能会成功建立,数据可能会一直通过,但我们仍然需要确保操作不会超过某个特定时间阈。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff"><em>HttpClient</em>没有任何允许我们为请求设置总超时的配置; 但它确实<strong>为请求</strong>提供了<strong>异常功能</strong>,所以我们可以利用该机制来实现一个简单的超时机制:</span></span></span><br />  </p> <pre> <code class="language-java">HttpGet getMethod = new HttpGet( "http://localhost:8080/spring-security-rest-template/api/bars/1"); int hardTimeout = 5; // seconds TimerTask task = new TimerTask() { @Override public void run() { if (getMethod != null) { getMethod.abort(); } } }; new Timer(true).schedule(task, hardTimeout * 1000); HttpResponse response = httpClient.execute(getMethod); System.out.println( "HTTP Status of response: " + response.getStatusLine().getStatusCode());</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">我们使用<em>java.util.Timer</em>和<em>java.util.TimerTask</em>来设置一个<strong>简单的延迟任务,</strong>在5秒硬超时后<strong>中止HTTP GET请求</strong>。</p> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">2.7</span></span></span><strong>超时和DNS循环 - 需要注意的事项</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">一些较大的域将使用DNS循环配置是非常常见的 - 基本上将<strong>相同的域映射到多个IP地址</strong>。由于HttpClient尝试连接到超时域的方式,这就给这个域的超时带来了新的挑战:</span></span></span></p> <ul style="list-style-type:disc"> <li>HttpClient获取到该域<strong>的IP路由列表</strong></li> <li>它会尝试<strong>第一个</strong> - 超时(使用我们配置的超时)</li> <li>它尝试<strong>第二个</strong> - 也超时</li> <li>等等 …</li> </ul> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">所以,正如你所看到的那样 - <strong>当我们预期它的整体操作不会超时</strong>。取而代之的是,当所有可能的路由超时以及更多的时候 - 它将完全透明地发生在客户端(除非你在DEBUG级别配置了你的日志)。以下是一个可以运行并复制此问题的简单示例:</span></span></span></p> <pre> <code class="language-java">int timeout = 3; RequestConfig config = RequestConfig.custom(). setConnectTimeout(timeout * 1000). setConnectionRequestTimeout(timeout * 1000). setSocketTimeout(timeout * 1000).build(); CloseableHttpClient client = HttpClientBuilder.create() .setDefaultRequestConfig(config).build(); HttpGet request = new HttpGet("http://www.google.com:81"); response = client.execute(request);</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">您将注意到具有DEBUG日志级别的重试逻辑:</span></span></span></p> <div style="text-align:start"> <div style="margin-left:0px; margin-right:0px"> <pre> <code class="language-html">DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.212:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.212:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.208:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.208:81 timed out. Connection will be retried using another IP address DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connecting to www.google.com/173.194.34.209:81 DEBUG o.a.h.i.c.HttpClientConnectionOperator - Connect to www.google.com/173.194.34.209:81 timed out. Connection will be retried using another IP address //... </code></pre> </div> </div> <h2><br /> 3.httpclient4<span style="font-family:raleway"><span style="color:#535353"><span style="background-color:#ffffff">取消/中止请求</span></span></span></h2> <h2>3.1<strong>中止GET请求</strong></h2> 要中止正在进行的请求,客户可以简单地使用: <pre> <code class="language-java">request.abort();</code></pre> 这将确保客户端不必使用整个请求来释放连接: <pre> <code class="language-java">@Test public void whenRequestIsCanceled_thenCorrect() throws ClientProtocolException, IOException { HttpClient instance = HttpClients.custom().build(); HttpGet request = new HttpGet(SAMPLE_URL); HttpResponse response = instance.execute(request); try { System.out.println(response.getStatusLine()); request.abort(); } finally { response.close(); } }</code></pre> <h2><span style="font-family:raleway"><span style="color:#535353"><span style="background-color:#ffffff">4.HttpClient 4不遵循重定向</span></span></span></h2> <h3>4.1 <strong>在HttpClient 4.3之前</strong></h3> 在旧版本的Http客户端(4.3版之前)中,我们可以配置客户端重定向的功能,如下所示: <pre> <code class="language-java">@Test public void givenRedirectsAreDisabled_whenConsumingUrlWhichRedirects_thenNotRedirected() throws ClientProtocolException, IOException { DefaultHttpClient instance = new DefaultHttpClient(); HttpParams params = new BasicHttpParams(); params.setParameter(ClientPNames.HANDLE_REDIRECTS, false); // HttpClientParams.setRedirecting(params, false); // alternative HttpGet httpGet = new HttpGet("http://t.co/I5YYd9tddw"); httpGet.setParams(params); CloseableHttpResponse response = instance.execute(httpGet); assertThat(response.getStatusLine().getStatusCode(), equalTo(301)); }</code></pre> 注意可以使用替代API来配置重定向行为,<strong>而不用设置实际的原始  <em>http.protocol.handle-redirects</em>参数</strong>: <pre> <code class="language-java">HttpClientParams.setRedirecting(params, false);</code></pre> 另外请注意,在禁用了后续重定向的情况下,我们现在可以检查Http Response状态代码是否确实是<em>301永久移动</em> - 因为它应该是。 <h3>4.2<strong>在HttpClient 4.3之后</strong></h3> <strong>HttpClient 4.3引入了更清洁,更高层次的API</strong>来构建和配置客户端: <pre> <code class="language-java">@Test public void givenRedirectsAreDisabled_whenConsumingUrlWhichRedirects_thenNotRedirected() throws ClientProtocolException, IOException { HttpClient instance = HttpClientBuilder.create().disableRedirectHandling().build(); HttpResponse response = instance.execute(new HttpGet("http://t.co/I5YYd9tddw")); assertThat(response.getStatusLine().getStatusCode(), equalTo(301)); }</code></pre> 请注意,新的API使用此重定向行为配置整个客户端 - 而不仅仅是个别请求。 <h2>5.httpclient4自定义请求头部信息</h2> <h3>5.1<strong>按要求设置标题 - 在4.3之前</strong></h3> 您可以使用<strong>简单的<em>setHeader</em>调用</strong>在请求上设置任何自定义标题: <pre> <code class="language-java">HttpClient client = new DefaultHttpClient(); HttpGet request = new HttpGet(SAMPLE_URL); request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");</code></pre> <h3>5.2<strong>请求标题 - 4.3及以上</strong></h3> HttpClient 4.3引入了一种构建请求的新方法 - <em>RequestBuilder</em>。要设置标题,我们将<strong>在构建器上</strong>使用相同的<strong><em>setHeader</em>方法</strong>: <pre> <code class="language-java">HttpClient client = HttpClients.custom().build(); HttpUriRequest request = RequestBuilder.get() .setUri(SAMPLE_URL) .setHeader(HttpHeaders.CONTENT_TYPE, "application/json") .build(); client.execute(request);</code></pre> <h3>5.3<strong>在客户端设置默认标题 - 4.3及以上</strong></h3> 除了在每个请求上设置标题,您还可以<strong>将其配置为客户端</strong>本身<strong>的默认标题</strong>: <pre> <code class="language-java">Header header = new BasicHeader( HttpHeaders.CONTENT_TYPE, "application/json"); List<Header> headers = Lists.newArrayList(header); HttpClient client = HttpClients.custom() .setDefaultHeaders(headers).build(); HttpUriRequest request = RequestBuilder.get() .setUri(SAMPLE_URL).build(); client.execute(request);</code></pre> 当所有请求的头部需要相同时,这非常有用 - 例如自定义应用程序头部。<br />   <h2>6.httpclient4.3版本以上常见操作</h2> <strong><span style="color:#800000">创建http客户端</span></strong> <pre> <code class="language-java">CloseableHttpClient client = HttpClientBuilder.create().build();</code></pre> <strong><span style="color:#800000">发送基本的GET请求</span></strong> <pre> <code class="language-java">instance.execute(new HttpGet("http://www.google.com"));</code></pre> <strong><span style="color:#800000">获取HTTP响应的状态码</span></strong> <pre> <code class="language-java">CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com")); assertThat(response.getStatusLine().getStatusCode(), equalTo(200));</code></pre> <strong><span style="color:#800000">获取响应的媒体类型</span></strong> <pre> <code class="language-java">CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com")); assertThat(response.getStatusLine().getStatusCode(), equalTo(200));</code></pre> <strong><span style="color:#800000">获取响应的媒体类型</span></strong> <pre> <code class="language-java">CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com")); String contentMimeType = ContentType.getOrDefault(response.getEntity()).getMimeType(); assertThat(contentMimeType, equalTo(ContentType.TEXT_HTML.getMimeType()));</code></pre> <strong><span style="color:#800000">得到响应的主体</span></strong> <pre> <code class="language-java">CloseableHttpResponse response = instance.execute(new HttpGet("http://www.google.com")); String bodyAsString = EntityUtils.toString(response.getEntity()); assertThat(bodyAsString, notNullValue());</code></pre> <strong>配置请求的超时时间</strong> <pre> <code class="language-java">@Test(expected = SocketTimeoutException.class) public void givenLowTimeout_whenExecutingRequestWithTimeout_thenException() throws ClientProtocolException, IOException { RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(1000).setConnectTimeout(1000).setSocketTimeout(1000).build(); HttpGet request = new HttpGet(SAMPLE_URL); request.setConfig(requestConfig); instance.execute(request); }</code></pre> <strong>在整个客户端上配置超时</strong> <pre> <code class="language-java">RequestConfig requestConfig = RequestConfig.custom(). setConnectionRequestTimeout(1000).setConnectTimeout(1000).setSocketTimeout(1000).build(); HttpClientBuilder builder = HttpClientBuilder.create().setDefaultRequestConfig(requestConfig); </code></pre> <strong><span style="color:#800000">发送POST请求</span></strong> <pre> <code class="language-java">instance.execute(new HttpPost(SAMPLE_URL));</code></pre> <strong><span style="color:#800000">将参数添加到请求</span></strong> <pre> <code class="language-java">List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("key1", "value1")); params.add(new BasicNameValuePair("key2", "value2")); request.setEntity(new UrlEncodedFormEntity(params, Consts.UTF_8));</code></pre> <strong>配置如何处理HTTP请求的重定向</strong> <pre> <code class="language-java">CloseableHttpClient instance = HttpClientBuilder.create().disableRedirectHandling().build(); CloseableHttpResponse response = instance.execute(new HttpGet("http://t.co/I5YYd9tddw")); assertThat(response.getStatusLine().getStatusCode(), equalTo(301));</code></pre> <strong><span style="color:#800000">为请求配置标题</span></strong> <pre> <code class="language-java">HttpGet request = new HttpGet(SAMPLE_URL); request.addHeader(HttpHeaders.ACCEPT, "application/xml"); response = instance.execute(request);</code></pre> <strong><span style="color:#800000">从响应中获取头文件</span></strong> <pre> <code class="language-java">CloseableHttpResponse response = instance.execute(new HttpGet(SAMPLE_URL)); Header[] headers = response.getHeaders(HttpHeaders.CONTENT_TYPE); assertThat(headers, not(emptyArray()));</code></pre> <strong><span style="color:#800000">关闭/释放资源</span></strong> <pre> <code class="language-java">response = instance.execute(new HttpGet(SAMPLE_URL)); try { HttpEntity entity = response.getEntity(); if (entity != null) { InputStream instream = entity.getContent(); instream.close(); } } finally { response.close(); }</code></pre>  
  • HttpClient Basic Authentication基本认证

    HttpClient Basic Authentication基本认证,本教程将说明如何在Apache HttpClient 4上配置基本身份验证。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本教程将说明如何<strong>在Apache HttpClient 4上配置基本身份验证</strong>。</span></span></span><br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.使用API​​进行基本身份验证</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">让我们从在HttpClient上配置基本认证的标准方式开始 - 通过<em>CredentialsProvider</em>:</span></span></span></p> <pre> <code class="language-java">CredentialsProvider provider = new BasicCredentialsProvider(); UsernamePasswordCredentials credentials = new UsernamePasswordCredentials("user1", "user1Pass"); provider.setCredentials(AuthScope.ANY, credentials); HttpClient client = HttpClientBuilder.create() .setDefaultCredentialsProvider(provider) .build(); HttpResponse response = client.execute( new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION)); int statusCode = response.getStatusLine() .getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK));</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">如您所见,使用凭证提供程序创建客户端以使用基本身份验证进行设置并不困难。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在,要了解<em>HttpClient</em>实际上在幕后做些什么,我们需要<strong>查看日志</strong>:</span></span></span></p> <pre> <code class="language-html"># ... request is sent with no credentials [main] DEBUG ... - Authentication required [main] DEBUG ... - localhost:8080 requested authentication [main] DEBUG ... - Authentication schemes in the order of preference: [negotiate, Kerberos, NTLM, Digest, Basic] [main] DEBUG ... - Challenge for negotiate authentication scheme not available [main] DEBUG ... - Challenge for Kerberos authentication scheme not available [main] DEBUG ... - Challenge for NTLM authentication scheme not available [main] DEBUG ... - Challenge for Digest authentication scheme not available [main] DEBUG ... - Selected authentication options: [BASIC] # ... the request is sent again - with credentials</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">整个<strong>客户端 - 服务器通信现在已经清楚</strong>:</span></span></span></p> <ul style="list-style-type:disc"> <li>客户端发送没有凭据的HTTP请求</li> <li>服务器发回一个挑战</li> <li>客户协商并确定正确的认证方案</li> <li>客户端发送<strong>第二个请求</strong>,这次是凭证</li> </ul> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.抢先式基本身份验证</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">开箱即用,HttpClient不会进行抢先认证 - 这必须由客户做出明确的决定。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">首先,我们需要创建<em>HttpContext</em> - 使用预先选择的正确类型的身份验证方案预先填充<strong>身份验证缓存</strong>。这将意味着前面示例中的否定不再是必要的 - <strong>已经选择了基本身份验证</strong>:</span></span></span></p> <pre> <code class="language-java">HttpHost targetHost = new HttpHost("localhost", 8080, "http"); CredentialsProvider credsProvider = new BasicCredentialsProvider(); credsProvider.setCredentials(AuthScope.ANY,   new UsernamePasswordCredentials(DEFAULT_USER, DEFAULT_PASS));   AuthCache authCache = new BasicAuthCache(); authCache.put(targetHost, new BasicScheme());   // Add AuthCache to the execution context final HttpClientContext context = HttpClientContext.create(); context.setCredentialsProvider(credsProvider); context.setAuthCache(authCache);</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">现在我们可以在新的上下文中使用客户端并<strong>发送预认证请求</strong>:</p> <pre> <code class="language-java">HttpClient client = HttpClientBuilder.create().build(); response = client.execute(   new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION), context);   int statusCode = response.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK));</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">我们来看看<strong>日志</strong>:</p> <pre> <code class="language-html">[main] DEBUG ... - Re-using cached 'basic' auth scheme for http://localhost:8080 [main] DEBUG ... - Executing request GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1 [main] DEBUG ... >> GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1 [main] DEBUG ... >> Host: localhost:8080 [main] DEBUG ... >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz [main] DEBUG ... << HTTP/1.1 200 OK [main] DEBUG ... - Authentication succeeded</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">一切看起来都OK:</span></span></span></p> <ul style="list-style-type:disc"> <li>“基本认证”方案是预选的</li> <li>该请求与<em>授权</em>标头一起发送</li> <li>服务器响应<em>200 OK</em></li> <li>身份验证成功</li> </ul> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>4.使用原始HTTP头进行基本身份验证</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">抢先式基本认证基本上意味着预先发送<em>授权</em>报头。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">因此,我们不必通过相当复杂的前一个示例来设置它,<strong>而是可以控制此标头</strong>并手动构建它:</span></span></span></p> <pre> <code class="language-java">HttpGet request = new HttpGet(URL_SECURED_BY_BASIC_AUTHENTICATION); String auth = DEFAULT_USER + ":" + DEFAULT_PASS; byte[] encodedAuth = Base64.encodeBase64(   auth.getBytes(Charset.forName("ISO-8859-1"))); String authHeader = "Basic " + new String(encodedAuth); request.setHeader(HttpHeaders.AUTHORIZATION, authHeader);   HttpClient client = HttpClientBuilder.create().build(); HttpResponse response = client.execute(request);   int statusCode = response.getStatusLine().getStatusCode(); assertThat(statusCode, equalTo(HttpStatus.SC_OK));</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">让我们确保这个工作正常:</p> <pre> <code class="language-html">[main] DEBUG ... - Auth cache not set in the context [main] DEBUG ... - Opening connection {}->http://localhost:8080 [main] DEBUG ... - Connecting to localhost/127.0.0.1:8080 [main] DEBUG ... - Executing request GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1 [main] DEBUG ... - Proxy auth state: UNCHALLENGED [main] DEBUG ... - http-outgoing-0 >> GET /spring-security-rest-basic-auth/api/foos/1 HTTP/1.1 [main] DEBUG ... - http-outgoing-0 >> Authorization: Basic dXNlcjE6dXNlcjFQYXNz [main] DEBUG ... - http-outgoing-0 << HTTP/1.1 200 OK </code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">因此,即使没有身份验证缓存,基本身份验证仍是正确执行,并<strong>在<em>200 OK</em>发送回</strong>。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>5.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本文介绍了使用Apache HttpClient 4设置和使用基本身份验证的各种方法。</span></span></span></p>
  • HttpClient 4.x POST请求(基础篇)

    在本教程中 - 我们将使用HttpClient 4进行POST - 首先使用授权,然后使用流畅的HttpClient API。最后 - 我们将讨论如何使用HttpClient上传文件。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在本教程中 - <strong>我们将使用<em>HttpClient</em> 4</strong>进行<strong>POST</strong> - 首先使用授权,然后使用流畅的HttpClient API。最后 - 我们将讨论如何使用<em>HttpClient</em>上传<em>文件</em>。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.基本POST</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">首先,我们<em>来看</em>一个简单的例子,并使用<em>HttpClient</em>发送POST请求。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在以下示例中 - 我们将使用两个参数 - “ <em>用户名</em> ”和“ <em>密码</em> ” 进行POST :</span></span></span></p> <pre> <code class="language-java">@Test public void whenPostRequestUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("username", "John")); params.add(new BasicNameValuePair("password", "pass")); httpPost.setEntity(new UrlEncodedFormEntity(params)); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">注意我们是如何使用的<em>列表</em>中<em>的NameValuePair</em>包括POST请求的参数。<br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.带授权的POST</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">接下来 - 让我们看看如何使用<em>HttpClient</em>执行带有身份验证凭证的POST 。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在下面的例子中 - 我们发送一个发布请求到一个使用基本身份验证保护的URL:</span></span></span></p> <pre> <code class="language-java">@Test public void whenPostRequestWithAuthorizationUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException, AuthenticationException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); httpPost.setEntity(new StringEntity("test post")); UsernamePasswordCredentials creds = new UsernamePasswordCredentials("John", "pass"); httpPost.addHeader(new BasicScheme().authenticate(creds, httpPost, null)); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>4.使用JSON进行POST</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在让我们看看如何使用<em>HttpClient</em>发送带有JSON正文的POST请求。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在以下示例中 - 我们将一些<em>人员</em>信息(<em>id,name</em>)作为JSON发送:</span></span></span></p> <pre> <code class="language-java">@Test public void whenPostJsonUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); String json = "{"id":1,"name":"John"}"; StringEntity entity = new StringEntity(json); httpPost.setEntity(entity); httpPost.setHeader("Accept", "application/json"); httpPost.setHeader("Content-type", "application/json"); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">请注意我们如何使用<em>StringEntity</em>来设置请求的主体。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">我们还将<em>ContentType</em>标头设置为<em>application / json,</em>以向服务器提供有关我们发送的内容表示的必要信息。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>5.使用<em>HttpClient Fluent API进行</em> POST</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">接下来 - 让我们使用<em>HttpClient </em><strong>Fluent API进行</strong> POST ; 我们将发送带有两个参数“ <em>用户名</em> ”和“ <em>密码</em> ” 的请求:</span></span></span></p> <pre> <code class="language-java">@Test public void whenPostFormUsingHttpClientFluentAPI_thenCorrect() throws ClientProtocolException, IOException { HttpResponse response = Request.Post("http://www.example.com").bodyForm( Form.form().add("username", "John").add("password", "pass").build()) .execute().returnResponse(); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>6. POST多部分请求</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在 - 让我们发布一个Multipart Request - 在下面的例子中,我们将使用<em>MultipartEntityBuilder</em>发布<em>文件</em>,用户名和密码:</span></span></span></p> <pre> <code class="language-java">@Test public void whenSendMultipartRequestUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addTextBody("username", "John"); builder.addTextBody("password", "pass"); builder.addBinaryBody("file", new File("test.txt"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); HttpEntity multipart = builder.build(); httpPost.setEntity(multipart); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>7. 使用<em>HttpClient</em>上传<em>文件</em></strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">接下来 - 让我们看看如何使用<em>HttpClient</em>上传<em>文件</em> - 我们将使用<em>MultipartEntityBuilder</em>上传“ <em>test.txt</em> ”文件:</span></span></span></p> <pre> <code class="language-java">@Test public void whenUploadFileUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addBinaryBody("file", new File("test.txt"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); HttpEntity multipart = builder.build(); httpPost.setEntity(multipart); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>8.获取<em>文件</em>上传</strong> <strong>进度</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">最后 - 让我们看看如何使用<em>HttpClient</em>获得<em>文件</em>上传的进度。在下面的例子中 - 我们将扩展<em>HttpEntityWrapper</em>以获取上传过程的可见性:</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">首先 - 这是上传方法:</span></span></span></p> <div style="text-align:start"> <div style="margin-left:0px; margin-right:0px">  <pre> <code class="language-java">@Test public void whenGetUploadFileProgressUsingHttpClient_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient client = HttpClients.createDefault(); HttpPost httpPost = new HttpPost("http://www.example.com"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); builder.addBinaryBody("file", new File("test.txt"), ContentType.APPLICATION_OCTET_STREAM, "file.ext"); HttpEntity multipart = builder.build(); ProgressEntityWrapper.ProgressListener pListener = new ProgressEntityWrapper.ProgressListener() { @Override public void progress(float percentage) { assertFalse(Float.compare(percentage, 100) > 0); } }; httpPost.setEntity(new ProgressEntityWrapper(multipart, pListener)); CloseableHttpResponse response = client.execute(httpPost); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); client.close(); }</code></pre> </div> </div> <p style="margin-left:0px; margin-right:0px; text-align:start">这里是接口<em>ProgressListener</em>,它<strong>使我们能够观察上传进度</strong>:<br />  </p> <pre> <code class="language-java">public static interface ProgressListener { void progress(float percentage); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">在这里我们扩展版本的<em>HttpEntityWrapper</em> “ <em>ProgressEntityWrapper</em> ”:<br />  </p> <pre> <code class="language-java">public class ProgressEntityWrapper extends HttpEntityWrapper { private ProgressListener listener; public ProgressEntityWrapper(HttpEntity entity, ProgressListener listener) { super(entity); this.listener = listener; } @Override public void writeTo(OutputStream outstream) throws IOException { super.writeTo(new CountingOutputStream(outstream, listener, getContentLength())); } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">和<em>FilterOutputStream</em>的扩展版本“ <em>CountingOutputStream</em> ”:<br /> <br />  </p> <pre> <code class="language-java">public static class CountingOutputStream extends FilterOutputStream { private ProgressListener listener; private long transferred; private long totalBytes; public CountingOutputStream( OutputStream out, ProgressListener listener, long totalBytes) { super(out); this.listener = listener; transferred = 0; this.totalBytes = totalBytes; } @Override public void write(byte[] b, int off, int len) throws IOException { out.write(b, off, len); transferred += len; listener.progress(getCurrentProgress()); } @Override public void write(int b) throws IOException { out.write(b); transferred++; listener.progress(getCurrentProgress()); } private float getCurrentProgress() { return ((float) transferred / totalBytes) * 100; } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">注意:</span></span></span></p> <ul style="list-style-type:disc"> <li>当将<em>FilterOutputStream</em>扩展为“ <em><em>CountingOutputStream</em> ”时 -</em>我们重写<em>write()</em>方法来计算写入(传输)的字节</li> <li>当将<em>HttpEntityWrapper</em>扩展为“ <em>ProgressEntityWrapper”时 -</em>我们重写<em>writeTo()</em>方法以使用我们的<em>“ <em>CountingOutputStream</em> ”</em></li> </ul> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>9.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在本教程中,我们演示了使用<em>Apache HttpClient 4</em>发送POST HTTP请求的最常用方式。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">我们学习了如何使用授权发送POST请求,如何发布使用<em>HttpClient </em><strong>fluent </strong><strong>API</strong>以及如何上传文件并跟踪其进度。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"> </p>
  • HttpClient 4 发送自定义的Cookie

    HttpClient 4 发送自定义的Cookie,本教程将重点介绍如何使用Apache HttpClient 4发送自定义Cookie。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本教程将重点介绍<strong>如何使用Apache HttpClient 4发送自定义Cookie</strong>。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.在HttpClient上配置Cookie管理</strong></h2> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.1。4.3之后的HttpClient</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在较新的HttpClient 4.3中,我们将利用负责构建和配置客户端的流利构建器API。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">首先,我们需要创建一个Cookie存储并在商店中设置我们的示例Cookie:</span></span></span></p> <pre> <code class="language-java">BasicCookieStore cookieStore = new BasicCookieStore(); BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", "1234"); cookie.setDomain(".github.com"); cookie.setPath("/"); cookieStore.addCookie(cookie);</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">然后,我们可以在HttpClient上设置这个cookie存储并发送请求:<br />  </p> <pre> <code class="language-java">@Test public void whenSettingCookiesOnTheHttpClient_thenCookieSentCorrectly() throws ClientProtocolException, IOException { BasicCookieStore cookieStore = new BasicCookieStore(); BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", "1234"); cookie.setDomain(".github.com"); cookie.setPath("/"); cookieStore.addCookie(cookie); HttpClient client = HttpClientBuilder.create().setDefaultCookieStore(cookieStore).build(); final HttpGet request = new HttpGet("http://www.github.com"); response = client.execute(request); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">一个非常重要的元素是<em><strong>域</strong></em>被设置上的Cookie - <strong>没有设置正确的域,客户端将不会发送该cookie,</strong>在所有!</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.2。4.3之前的HttpClient</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">使用旧版本的HttpClient(4.3之前) - cookie存储直接在<em>HttpClient</em>上设置:</span></span></span></p> <pre> <code class="language-java">@Test public void givenUsingDeprecatedApi_whenSettingCookiesOnTheHttpClient_thenCorrect() throws ClientProtocolException, IOException { BasicCookieStore cookieStore = new BasicCookieStore(); BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", "1234"); cookie.setDomain(".github.com"); cookie.setPath("/"); cookieStore.addCookie(cookie); DefaultHttpClient client = new DefaultHttpClient(); client.setCookieStore(cookieStore); HttpGet request = new HttpGet("http://www.github.com"); response = client.execute(request); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">除了客户端的构建方式之外,与前面的示例没有其他区别。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.在请求中设置Cookie</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">如果在整个HttpClient上设置cookie不是一个选项,我们可以单独配置cookie的请求:</span></span></span></p> <pre> <code class="language-java">@Test public void whenSettingCookiesOnTheRequest_thenCookieSentCorrectly() throws ClientProtocolException, IOException { BasicCookieStore cookieStore = new BasicCookieStore(); BasicClientCookie cookie = new BasicClientCookie("JSESSIONID", "1234"); cookie.setDomain(".github.com"); cookie.setPath("/"); cookieStore.addCookie(cookie); instance = HttpClientBuilder.create().build(); HttpGet request = new HttpGet("http://www.github.com"); HttpContext localContext = new BasicHttpContext(); localContext.setAttribute(HttpClientContext.COOKIE_STORE, cookieStore); // localContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore); // before 4.3 response = instance.execute(request, localContext); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>4.在低层请求中设置Cookie</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在HTTP请求中设置Cookie的低级替代方案将会将其设置为原始标题:</span></span></span></p> <pre> <code class="language-java">@Test public void whenSettingCookiesOnARequest_thenCorrect() throws ClientProtocolException, IOException { instance = HttpClientBuilder.create().build(); HttpGet request = new HttpGet("http://www.github.com"); request.setHeader("Cookie", "JSESSIONID=1234"); response = instance.execute(request); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff"><strong>与使用内置cookie支持相比,</strong>这当然更<strong>容易出错</strong> - 例如,注意我们在这种情况下不再设置域 - 这是不正确的。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>5.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本文说明了如何<strong>使用HttpClient发送自定义的,用户控制的Cookie</strong>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">请注意,这与让HttpClient处理由服务器设置的cookie不同 - 但是它却是在低级别手动控制客户端。</span></span></span></p>
  • HttpClient 4 按照POST重定向请求

    HttpClient 4 按照POST重定向请求,本快速教程将展示如何配置Apache HttpClient 4以自动遵循POST请求的重定向。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本快速教程将展示如何配置Apache HttpClient 4以自动遵循POST请求的重定向。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">默认情况下,只有自动跟随导致重定向的GET请求。如果POST请求被<em>HTTP 301永久移动</em>或<em>302找到</em> - <strong>则不会自动执行重定向</strong>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">这由<a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3" rel="external nofollow" style="box-sizing:border-box; color:#63b175; text-decoration:none" target="_blank" title="重定向3xx">HTTP RFC 2616</a>指定:</span></span></span></p> <blockquote> <p><span style="color:#535353">如果接收到301状态代码以响应除GET或HEAD以外的请求,则用户代理绝不能自动重定向请求,除非用户可以确认它,因为这可能会改变发出请求的条件。</span></p> </blockquote> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">当然,我们需要改变这种行为并放宽严格的HTTP规范。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">首先,我们来检查一下默认行为:</span></span></span></p> <pre> <code class="language-java">@Test public void givenPostRequest_whenConsumingUrlWhichRedirects_thenNotRedirected() throws ClientProtocolException, IOException { HttpClient instance = HttpClientBuilder.create().build(); HttpResponse response = instance.execute(new HttpPost("http://t.co/I5YYd9tddw")); assertThat(response.getStatusLine().getStatusCode(), equalTo(301)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">正如你所看到的,<strong>重定向并不是默认的</strong>,我们得到了<em>301状态码</em>。</span></span></span></p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.在HTTP POST上重定向</strong></h2> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.1。对于HttpClient 4.3和之后</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在HttpClient 4.3中,为客户端的创建和配置引入了更高级别的API:</span></span></span></p> <pre> <code class="language-java">@Test public void givenRedirectingPOST_whenConsumingUrlWhichRedirectsWithPOST_thenRedirected() throws ClientProtocolException, IOException { HttpClient instance = HttpClientBuilder.create().setRedirectStrategy(new LaxRedirectStrategy()).build(); HttpResponse response = instance.execute(new HttpPost("http://t.co/I5YYd9tddw")); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">注意<strong>的<em>HttpClientBuilder</em>现在是一个流利API的起点</strong>,其允许在比以前更可读的方式在客户端的完整配置。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.2。对于HttpClient 4.2</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在之前版本的HttpClient(4.2)中,我们可以直接在客户端上配置重定向策略:</span></span></span></p> <pre> <code class="language-java">@SuppressWarnings("deprecation") @Test public void givenRedirectingPOST_whenConsumingUrlWhichRedirectsWithPOST_thenRedirected() throws ClientProtocolException, IOException { DefaultHttpClient client = new DefaultHttpClient(); client.setRedirectStrategy(new LaxRedirectStrategy()); HttpResponse response = client.execute(new HttpPost("http://t.co/I5YYd9tddw")); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">请注意,现在,使用新的<em>LaxRedirectStrategy</em>,HTTP限制被放宽,<strong>重定向也通过POST进行</strong> - 从而导致<em>200 OK</em>状态码。</span></span></span></p> <h3 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2.3。Pre HttpClient 4.2</strong></h3> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在HttpClient 4.2之前,<em>LaxRedirectStrategy</em>类不存在,所以我们需要滚动我们自己的:</span></span></span></p> <pre> <code class="language-java">@Test public void givenRedirectingPOST_whenConsumingUrlWhichRedirectsWithPOST_thenRedirected()   throws ClientProtocolException, IOException {     DefaultHttpClient client = new DefaultHttpClient();     client.setRedirectStrategy(new DefaultRedirectStrategy() {         /** Redirectable methods. */         private String[] REDIRECT_METHODS = new String[] {             HttpGet.METHOD_NAME, HttpPost.METHOD_NAME, HttpHead.METHOD_NAME         };           @Override         protected boolean isRedirectable(String method) {             for (String m : REDIRECT_METHODS) {                 if (m.equalsIgnoreCase(method)) {                     return true;                 }             }             return false;         }     });       HttpResponse response = client.execute(new HttpPost("http://t.co/I5YYd9tddw"));     assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre>   <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本快速指南说明了如何配置Apache HttpClient 4的任何版本以遵循HTTP POST请求的重定向 - 放宽严格的HTTP标准。</span></span></span></p>
  • httpclient4.5使用详解 httpclient 4.5 post传递json参数

    httpclient4.5使用详解 httpclient 4.5 post传递json参数httpclient4.5使用详解 httpclient 4.5 post传递json参数 <pre> <code class="language-java">import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; 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.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.entity.StringEntity; 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; /** * 工具类 httpclient4.5 * * @author xq * */ public class HttpclientUtils { /** * post请求 json参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doPost(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-Type", "application/json"); httpPost.setEntity(new StringEntity(bodyJsonParams)); if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpPost.addHeader(key, value); } } return execute(httpPost); } /** * post k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doPost(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); if (params != null && params.keySet().isEmpty()) { List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); list.add(new BasicNameValuePair(key, value)); } httpPost.setEntity(new UrlEncodedFormEntity(list)); } if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpPost.addHeader(key, value); } } return execute(httpPost); } /** * get请求 * * @param url * @param params * @param headers * @return * @throws IOException * @throws ClientProtocolException */ public static String doGet(String url, Map<String, String> params, Map<String, String> headers) throws IOException { // 参数 StringBuilder paramsBuilder = new StringBuilder(url); if (params != null && params.keySet().isEmpty()) { if (url.indexOf("?") == -1) { paramsBuilder.append("?"); } List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); list.add(new BasicNameValuePair(key, value)); } String paramsStr = EntityUtils.toString(new UrlEncodedFormEntity(list)); paramsBuilder.append(paramsStr); } HttpGet httpGet = new HttpGet(paramsBuilder.toString()); // 头 if (headers != null && headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpGet.addHeader(key, value); } } return execute(httpGet); } /** * 执行请求并返回string值 * * @param httpUriRequest * @return * @throws IOException */ private static String execute(HttpUriRequest httpUriRequest) throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { CloseableHttpResponse response = httpClient.execute(httpUriRequest); if (response.getStatusLine().getStatusCode() == 200) {// 请求成功状态 try (BufferedReader bufferedReader = new BufferedReader( new InputStreamReader(response.getEntity().getContent()))) { String result=""; String tmp=null; while((tmp=bufferedReader.readLine())!=null){ result+=tmp; } return result; } } } return null; } } </code></pre>  
  • HttpClient的RestTemplate - Java配置示例

    HttpClient的RestTemplate - Java配置示例<h2 style="margin-left:0px; margin-right:0px; text-align:start">HttpClient配置</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在<code>HttpClientConfig</code>课堂上,我们主要配置两件事 -</span></span></span></p> <ol style="margin-left:40px; margin-right:0px"> <li style="list-style-type:decimal"><code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/conn/PoolingHttpClientConnectionManager.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">PoolingHttpClientConnectionManager</a></code> - 顾名思义,它的连接池管理器。在这里,连接按照每个路线进行汇集。对于已经是管理器具有可用于池中的持续连接的路由的请求将是通过从池租用连接而不是创建全新连接的服务。 <p style="margin-left:0px; margin-right:0px"><code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/conn/ConnectionKeepAliveStrategy.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ConnectionKeepAliveStrategy</a></code> 有助于设置时间,这决定了连接在重新使用之前可以保持空闲状态的时间。</p> </li> <li style="list-style-type:decimal">并设置一个<code>idleConnectionMonitor</code>线程,它会定期检查所有连接并释放尚未使用的空闲时间和空闲时间。</li> </ol> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">真正使用的http客户端是<code><a href="https://hc.apache.org/httpcomponents-client-ga/httpclient/apidocs/org/apache/http/impl/client/CloseableHttpClient.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">CloseableHttpClient</a></code>bean。它将<code>RestTemplate</code>用于获取与API端点的连接。</span></span></span><br />  </p> <pre> <code class="language-java">import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.concurrent.TimeUnit; import org.apache.http.HeaderElement; import org.apache.http.HeaderElementIterator; import org.apache.http.HttpResponse; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.ConnectionKeepAliveStrategy; 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.conn.ssl.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicHeaderElementIterator; import org.apache.http.protocol.HTTP; import org.apache.http.protocol.HttpContext; import org.apache.http.ssl.SSLContextBuilder; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.Scheduled; /** * - Supports both HTTP and HTTPS * - Uses a connection pool to re-use connections and save overhead of creating connections. * - Has a custom connection keep-alive strategy (to apply a default keep-alive if one isn't specified) * - Starts an idle connection monitor to continuously clean up stale connections. */ @Configuration @EnableScheduling public class HttpClientConfig { private static final Logger LOGGER = LoggerFactory.getLogger(HttpClientConfig.class); // Determines the timeout in milliseconds until a connection is established. private static final int CONNECT_TIMEOUT = 30000; // The timeout when requesting a connection from the connection manager. private static final int REQUEST_TIMEOUT = 30000; // The timeout for waiting for data private static final int SOCKET_TIMEOUT = 60000; private static final int MAX_TOTAL_CONNECTIONS = 50; private static final int DEFAULT_KEEP_ALIVE_TIME_MILLIS = 20 * 1000; private static final int CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS = 30; @Bean public PoolingHttpClientConnectionManager poolingConnectionManager() { SSLContextBuilder builder = new SSLContextBuilder(); try { builder.loadTrustMaterial(null, new TrustSelfSignedStrategy()); } catch (NoSuchAlgorithmException | KeyStoreException e) { LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e); } SSLConnectionSocketFactory sslsf = null; try { sslsf = new SSLConnectionSocketFactory(builder.build()); } catch (KeyManagementException | NoSuchAlgorithmException e) { LOGGER.error("Pooling Connection Manager Initialisation failure because of " + e.getMessage(), e); } Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .<ConnectionSocketFactory>create().register("https", sslsf) .register("http", new PlainConnectionSocketFactory()) .build(); PoolingHttpClientConnectionManager poolingConnectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); poolingConnectionManager.setMaxTotal(MAX_TOTAL_CONNECTIONS); return poolingConnectionManager; } @Bean public ConnectionKeepAliveStrategy connectionKeepAliveStrategy() { return new ConnectionKeepAliveStrategy() { @Override public long getKeepAliveDuration(HttpResponse response, HttpContext context) { HeaderElementIterator it = new BasicHeaderElementIterator (response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (it.hasNext()) { HeaderElement he = it.nextElement(); String param = he.getName(); String value = he.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { return Long.parseLong(value) * 1000; } } return DEFAULT_KEEP_ALIVE_TIME_MILLIS; } }; } @Bean public CloseableHttpClient httpClient() { RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(REQUEST_TIMEOUT) .setConnectTimeout(CONNECT_TIMEOUT) .setSocketTimeout(SOCKET_TIMEOUT).build(); return HttpClients.custom() .setDefaultRequestConfig(requestConfig) .setConnectionManager(poolingConnectionManager()) .setKeepAliveStrategy(connectionKeepAliveStrategy()) .build(); } @Bean public Runnable idleConnectionMonitor(final PoolingHttpClientConnectionManager connectionManager) { return new Runnable() { @Override @Scheduled(fixedDelay = 10000) public void run() { try { if (connectionManager != null) { LOGGER.trace("run IdleConnectionMonitor - Closing expired and idle connections..."); connectionManager.closeExpiredConnections(); connectionManager.closeIdleConnections(CLOSE_IDLE_CONNECTION_WAIT_TIME_SECS, TimeUnit.SECONDS); } else { LOGGER.trace("run IdleConnectionMonitor - Http Client Connection manager is not initialised"); } } catch (Exception e) { LOGGER.error("run IdleConnectionMonitor - Exception occurred. msg={}, e={}", e.getMessage(), e); } } }; } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">RestTemplate配置</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">在这里,我们正在配置<code>RestTemplate</code>我们最终将用来调用REST API的bean。如上所述,它使用<code>CloseableHttpClient</code>bean实例来构建<code><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/ClientHttpRequestFactory.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ClientHttpRequestFactory</a></code>,用于创建<code>RestTemplate</code>。</span></span></span></p> <ol style="margin-left:40px; margin-right:0px"> <li style="list-style-type:decimal"><code><a href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/client/HttpComponentsClientHttpRequestFactory.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">HttpComponentsClientHttpRequestFactory</a></code>是<code>ClientHttpRequestFactory</code>使用<em>Apache HttpComponents HttpClient</em>创建请求的实现。</li> <li style="list-style-type:decimal">我们<code>@Scheduled</code>在<code>httpClient</code>配置中使用了注释。为了支持这个,我们必须添加对线程预定执行的支持。为此,我们使用了<code>ThreadPoolTaskScheduler</code>内部使用<a href="https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html" rel="external nofollow" style="box-sizing:border-box; transition:all 0.1s ease-in-out; color:#0366d6; text-decoration:none" target="_blank">ScheduledThreadPoolExecutor的</a> bean 来安排命令在给定的延迟后运行,或者定期执行。</li> </ol> <pre> <code class="language-java">import org.apache.http.impl.client.CloseableHttpClient; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.scheduling.TaskScheduler; import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; import org.springframework.web.client.RestTemplate; public class RestTemplateConfig { @Autowired CloseableHttpClient httpClient; @Bean public RestTemplate restTemplate() { RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory()); return restTemplate; } @Bean public HttpComponentsClientHttpRequestFactory clientHttpRequestFactory() { HttpComponentsClientHttpRequestFactory clientHttpRequestFactory = new HttpComponentsClientHttpRequestFactory(); clientHttpRequestFactory.setHttpClient(httpClient); return clientHttpRequestFactory; } @Bean public TaskScheduler taskScheduler() { ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler(); scheduler.setThreadNamePrefix("poolScheduler"); scheduler.setPoolSize(50); return scheduler; } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">如何使用RestTemplate</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">要使用上述配置<code>RestTemplate</code>,只需将其注入控制器或测试类。</span></span></span></p> <pre> <code class="language-java">import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.client.RestTemplate; import com.leftso.config.HttpClientConfig; import com.leftso.config.RestTemplateConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { RestTemplateConfig.class, HttpClientConfig.class }) public class TestApplication { @Autowired RestTemplate restTemplate; @Test public void getEmployees() { final String uri = "http://localhost:8080/employees"; String result = restTemplate.getForObject(uri, String.class); Assert.assertEquals(true, result.indexOf("Lokesh") > 0); } }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start">Maven的依赖</h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#333333"><span style="font-family:"varela round","helvetica neue",Helvetica,Arial,sans-serif"><span style="background-color:#ffffff">首先,你将被要求有两个依赖关系,即<code>httpclient</code>和<code>spring-web</code>。我正在使用spring启动应用程序,所以pom文件如下所示:</span></span></span></p> <pre> <code class="language-xml"><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>com.leftso</groupId> <artifactId>springbootdemo</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>springbootdemo</name> <url>http://maven.apache.org</url> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.0.RELEASE</version> </parent> <properties> <java.version>1.8</java.version> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-hateoas</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <dependency> <groupId>org.apache.httpcomponents</groupId> <artifactId>httpclient</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project></code></pre>
  • HttpClient 使用SSL访问HTTPS(提高篇)

    本文将展示如何使用“接受所有”SSL支持来配置Apache HttpClient 4。目标很简单 - 使用没有有效证书的HTTPS URL。<h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>1.概述</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本文将展示如何<strong>使用“接受所有”SSL支持</strong>来<strong>配置Apache HttpClient 4</strong>。目标很简单 - 使用没有有效证书的HTTPS URL。</span></span></span><br /> <br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>2. <em>SSLPeerUnverifiedException</em></strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在没有使用<em>HttpClient</em>配置SSL的情况下,以下测试(使用HTTPS URL)将失败:</span></span></span></p> <pre> <code class="language-java">public class HttpLiveTest { @Test(expected = SSLPeerUnverifiedException.class) public void whenHttpsUrlIsConsumed_thenException() throws ClientProtocolException, IOException { DefaultHttpClient httpClient = new DefaultHttpClient(); String urlOverHttps = "https://localhost:8080/spring-security-rest-basic-auth"; HttpGet getMethod = new HttpGet(urlOverHttps); HttpResponse response = httpClient.execute(getMethod); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); } }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">确切的失败是:</p> <pre> <code class="language-java">javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated at sun.security.ssl.SSLSessionImpl.getPeerCertificates(SSLSessionImpl.java:397) at org.apache.http.conn.ssl.AbstractVerifier.verify(AbstractVerifier.java:126) ...</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">该<a href="http://docs.oracle.com/javase/7/docs/api/javax/net/ssl/SSLPeerUnverifiedException.html" rel="external nofollow" style="box-sizing:border-box; background-color:#ffffff; color:#63b175; text-decoration:none; font-family:raleway; font-size:18px; font-style:normal; font-variant-ligatures:normal; font-variant-caps:normal; font-weight:normal; letter-spacing:normal; orphans:2; text-align:start; text-transform:none; white-space:normal; widows:2; word-spacing:0px; -webkit-text-stroke-width:0px" target="_blank" title="Java SE 7中的SSLPeerUnverifiedException javadoc"><em>javax.net.ssl.SSLPeerUnverifiedException</em>例外</a>,只要有效信任链无法为URL建立发生。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>3.配置SSL - 全部接受(HttpClient <4.3)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在让我们将HTTP客户端配置为信任所有证书链,而不管它们的有效性如何:</span></span></span></p> <pre> <code class="language-java">@Test public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() throws IOException, GeneralSecurityException { TrustStrategy acceptingTrustStrategy = (cert, authType) -> true; SSLSocketFactory sf = new SSLSocketFactory( acceptingTrustStrategy, SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); SchemeRegistry registry = new SchemeRegistry(); registry.register(new Scheme("https", 8443, sf)); ClientConnectionManager ccm = new PoolingClientConnectionManager(registry); DefaultHttpClient httpClient = new DefaultHttpClient(ccm); String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; HttpGet getMethod = new HttpGet(urlOverHttps); HttpResponse response = httpClient.execute(getMethod); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">随着新的<em>TrustStrategy</em>现在<strong>覆盖标准证书验证过程</strong>(应该咨询配置的信任管理器) - 测试现在通过,<strong>客户端可以使用HTTPS URL</strong>。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>4. 带SSL 的Spring <em>RestTemplate</em>(HttpClient <4.3)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">现在我们已经看到了如何配置一个支持SSL 的原始<em>HttpClient</em>,让我们来看看更高级别的客户端<em>--Spring RestTemplate</em>。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">未配置SSL,以下测试将按预期失败:</span></span></span></p> <pre> <code class="language-java">@Test(expected = ResourceAccessException.class) public void whenHttpsUrlIsConsumed_thenException() { String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; ResponseEntity<String> response = new RestTemplate().exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">那么让我们来配置SSL:<br />  </p> <pre> <code class="language-java">import static org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER; import java.security.GeneralSecurityException; import java.security.cert.X509Certificate; import org.apache.http.auth.AuthScope; import org.apache.http.auth.UsernamePasswordCredentials; import org.apache.http.conn.scheme.Scheme; import org.apache.http.conn.ssl.SSLSocketFactory; import org.apache.http.conn.ssl.TrustStrategy; import org.apache.http.impl.client.DefaultHttpClient; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.web.client.ResourceAccessException; import org.springframework.web.client.RestTemplate; ... @Test public void givenAcceptingAllCertificates_whenHttpsUrlIsConsumed_thenException() throws GeneralSecurityException { HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); DefaultHttpClient httpClient = (DefaultHttpClient) requestFactory.getHttpClient(); TrustStrategy acceptingTrustStrategy = (cert, authType) -> true SSLSocketFactory sf = new SSLSocketFactory( acceptingTrustStrategy, ALLOW_ALL_HOSTNAME_VERIFIER); httpClient.getConnectionManager().getSchemeRegistry() .register(new Scheme("https", 8443, sf)); String urlOverHttps = "https://localhost:8443/spring-security-rest-basic-auth/api/bars/1"; ResponseEntity<String> response = new RestTemplate(requestFactory). exchange(urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <p style="margin-left:0px; margin-right:0px; text-align:start">如您所见,这<strong>与我们为原始HttpClient配置SSL的方式非常相似</strong> - 我们使用SSL支持配置请求工厂,然后实例化通过此预配置工厂的模板。<br />  </p> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>5.配置SSL - 全部接受(HttpClient 4.4)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">在HttpClient 4.4版本中,现在不推荐使用<em>SSLSocketFactory</em>,我们可以简单地配置我们的<em>HttpClient</em>,如下所示:</span></span></span></p> <pre> <code class="language-java">@Test public void givenIgnoringCertificates_whenHttpsUrlIsConsumed_thenCorrect() throws Exception { SSLContext sslContext = new SSLContextBuilder() .loadTrustMaterial(null, (certificate, authType) -> true).build(); CloseableHttpClient client = HttpClients.custom() .setSSLContext(sslContext) .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); HttpGet httpGet = new HttpGet(HOST_WITH_SSL); httpGet.setHeader("Accept", "application/xml"); HttpResponse response = client.execute(httpGet); assertThat(response.getStatusLine().getStatusCode(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>6. 带SSL 的Spring <em>RestTemplate</em>(HttpClient 4.4)</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">我们可以用同样的方法来配置我们的<em>RestTemplate</em>:</span></span></span></p> <pre> <code class="language-java">@Test public void givenAcceptingAllCertificatesUsing4_4_whenUsingRestTemplate_thenCorrect() throws ClientProtocolException, IOException { CloseableHttpClient httpClient = HttpClients.custom() .setSSLHostnameVerifier(new NoopHostnameVerifier()) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(); requestFactory.setHttpClient(httpClient); ResponseEntity<String> response = new RestTemplate(requestFactory).exchange( urlOverHttps, HttpMethod.GET, null, String.class); assertThat(response.getStatusCode().value(), equalTo(200)); }</code></pre> <h2 style="margin-left:0px; margin-right:0px; text-align:start"><strong>7.总结</strong></h2> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">本教程讨论了如何为Apache HttpClient配置SSL,以便它能够使用任何HTTPS URL,而不考虑证书。还展示了Spring <em>RestTemplate</em>的相同配置。</span></span></span></p> <p style="margin-left:0px; margin-right:0px; text-align:start"><span style="color:#535353"><span style="font-family:raleway"><span style="background-color:#ffffff">然而,要理解的重要一点是,<strong>这种策略完全忽略了证书检查</strong> - 这使得它不安全,只能在有意义的地方使用。</span></span></span></p>
  • Apache httpclient4.5 GET/POST/PUT/OPTION/DELETE工具类

    Apache httpclient4.5 GET/POST/PUT/OPTION/DELETE工具类Apache httpclient4.5 GET/POST/PUT/OPTION/DELETE工具类<br /> <br /> 该工具类使用比较新的<br /> Apache httpclient4.5版本<br /> <br /> 支持GET请求、POST请求、DELETE请求、PATCH请求、PUT请求、DELETE请求、OPTIONS请求<br /> <br /> 工具类代码如下: <pre> <code class="language-java"> import org.apache.http.Header; import org.apache.http.NameValuePair; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.*; import org.apache.http.entity.StringEntity; 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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.StringUtils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URI; import java.nio.charset.Charset; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 工具类 httpclient4.5 * * @author xq */ public class HttpclientUtils { private final static Logger logger = LoggerFactory.getLogger(HttpclientUtilss.class); /** * post请求 json参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doPost(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); httpPost.addHeader("Content-Type", "application/json"); httpPost.setEntity(new StringEntity(bodyJsonParams, Charset.forName("UTF-8"))); addHeader(httpPost, headers); return execute(httpPost); } /** * post k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doPost(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpPost httpPost = new HttpPost(url); if (params != null && params.keySet().isEmpty()) { httpPost.setEntity(getUrlEncodedFormEntity(params)); } addHeader(httpPost, headers); return execute(httpPost); } /** * patch json参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doPatch(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpPatch httpPatch = new HttpPatch(url); httpPatch.setEntity(new StringEntity(bodyJsonParams)); addHeader(httpPatch, headers); return execute(httpPatch); } /** * patch k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doPatch(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpPatch httpPatch = new HttpPatch(url); if (params != null && !params.isEmpty()) { httpPatch.setEntity(getUrlEncodedFormEntity(params)); } addHeader(httpPatch, headers); return execute(httpPatch); } /** * PUT JSON参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doPut(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpPut httpPut = new HttpPut(url); httpPut.addHeader("Content-Type", "application/json"); httpPut.setEntity(new StringEntity(bodyJsonParams, Charset.forName("UTF-8"))); addHeader(httpPut, headers); return execute(httpPut); } /** * put k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doPut(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpPut httpPut = new HttpPut(url); if (params != null && params.keySet().isEmpty()) { httpPut.setEntity(getUrlEncodedFormEntity(params)); } addHeader(httpPut, headers); return execute(httpPut); } /** * Delete json 参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doDeletedoPut(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpDeleteWithEntity httpDelete = new HttpDeleteWithEntity(url); httpDelete.setEntity(new StringEntity(bodyJsonParams)); addHeader(httpDelete, headers); return execute(httpDelete); } /** * delete k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doDelete(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpDeleteWithEntity httpDelete = new HttpDeleteWithEntity(url); addHeader(httpDelete, headers); if (params != null && !params.isEmpty()) { httpDelete.setEntity(getUrlEncodedFormEntity(params)); } return execute(httpDelete); } /** * options json参数 * * @param url * @param bodyJsonParams * @param headers * @return * @throws IOException */ public static String doOptions(String url, String bodyJsonParams, Map<String, String> headers) throws IOException { HttpOptionsWithEntity httpOptions = new HttpOptionsWithEntity(url); addHeader(httpOptions, headers); httpOptions.setEntity(new StringEntity(bodyJsonParams)); return execute(httpOptions); } /** * options k-v参数 * * @param url * @param params * @param headers * @return * @throws IOException */ public static String doOptions(String url, Map<String, String> params, Map<String, String> headers) throws IOException { HttpOptionsWithEntity httpOptions = new HttpOptionsWithEntity(url); addHeader(httpOptions, headers); if (params != null && !params.isEmpty()) { httpOptions.setEntity(getUrlEncodedFormEntity(params)); } return execute(httpOptions); } /** * head请求 * * @param url * @param headers * @return * @throws IOException */ public static String doHeader(String url, Map<String, String> headers) throws IOException { HttpHead httpHead = new HttpHead(url); addHeader(httpHead, headers); return execute(httpHead); } /** * get请求 * * @param url * @param params * @param headers * @return * @throws IOException * @throws ClientProtocolException */ public static String doGet(String url, Map<String, String> params, Map<String, String> headers) throws IOException { // 参数 StringBuilder paramsBuilder = new StringBuilder(url); if (params != null && params.keySet().isEmpty()) { if (url.indexOf("?") == -1) { paramsBuilder.append("?"); } List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); list.add(new BasicNameValuePair(key, value)); } String paramsStr = EntityUtils.toString(new UrlEncodedFormEntity(list)); paramsBuilder.append(paramsStr); } HttpGet httpGet = new HttpGet(paramsBuilder.toString()); // 头 addHeader(httpGet, headers); return execute(httpGet); } /** * 执行请求并返回string值 * * @param httpUriRequest * @return * @throws IOException */ private static String execute(HttpUriRequest httpUriRequest) throws IOException { try (CloseableHttpClient httpClient = HttpClients.createDefault()) { CloseableHttpResponse response = httpClient.execute(httpUriRequest); // if (response.getStatusLine().getStatusCode() == 200) {// 请求成功状态 // try (BufferedReader bufferedReader = new BufferedReader( // new InputStreamReader(response.getEntity().getContent()))) { // StringBuilder sb = new StringBuilder(); // String tmp; // while ((tmp = bufferedReader.readLine()) != null) { // sb.append(tmp); // } // return sb.toString(); // } // } Header type = response.getEntity().getContentType(); logger.debug("Type:" + type.getValue()); String defaultCharset = "UTF-8"; String charset = getCharSet(type.getValue()); if (!StringUtils.isEmpty(charset)) { defaultCharset = charset; } return EntityUtils.toString(response.getEntity(), defaultCharset); } // return null; } /** * 添加请求头部 * * @param httpUriRequest * @param headers */ private static void addHeader(HttpUriRequest httpUriRequest, Map<String, String> headers) { if (httpUriRequest != null) { if (headers != null && !headers.keySet().isEmpty()) { Set<String> keySet = headers.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = headers.get(key); httpUriRequest.addHeader(key, value); } } } } /** * 获取 UrlEncodedFormEntity 参数实体 * * @param params * @return * @throws UnsupportedEncodingException */ private static UrlEncodedFormEntity getUrlEncodedFormEntity(Map<String, String> params) throws UnsupportedEncodingException { if (params != null && params.keySet().isEmpty()) { List<NameValuePair> list = new ArrayList<>(); Set<String> keySet = params.keySet(); Iterator<String> iterator = keySet.iterator(); while (iterator.hasNext()) { String key = iterator.next(); String value = params.get(key); list.add(new BasicNameValuePair(key, value)); } return new UrlEncodedFormEntity(list); } return null; } /** * 根据HTTP 响应头部的content type抓取响应的字符集编码 * * @param content * @return */ private static String getCharSet(String content) { String regex = ".*charset=([^;]*).*"; Pattern pattern = Pattern.compile(regex); Matcher matcher = pattern.matcher(content); if (matcher.find()) return matcher.group(1); else return null; } /** * 解决httpclient 的DELETE默认不支持setEntity */ static class HttpDeleteWithEntity extends HttpEntityEnclosingRequestBase { public static final String METHOD_NAME = "DELETE"; @Override public String getMethod() { return METHOD_NAME; } public HttpDeleteWithEntity(final String uri) { super(); setURI(URI.create(uri)); } public HttpDeleteWithEntity(final URI uri) { super(); setURI(uri); } public HttpDeleteWithEntity() { super(); } } /** * 解决httpclient 的OPTIONS默认不支持setEntity */ static class HttpOptionsWithEntity extends HttpEntityEnclosingRequestBase { public static final String METHOD_NAME = "OPTIONS"; @Override public String getMethod() { return METHOD_NAME; } public HttpOptionsWithEntity() { super(); } public HttpOptionsWithEntity(final String uri) { super(); setURI(URI.create(uri)); } public HttpOptionsWithEntity(final URI uri) { super(); setURI(uri); } } public static void main(String[] args) { } } </code></pre>  
  • Angular HttpClient使用RxJS Observable例子

    Angular HttpClient使用RxJS Observable例子,习使用Angular2  HttpClient服务从在线REST API获取数据并将其作为Observable对象/数组返回。任何数据事件,订阅者observable都会做出反应。Angular2 HttpClient使用RxJS Observable例子