httpClient-3.1学习笔记

http://hc.apache.org/httpclient-3.x/tutorial.html
The general process for using HttpClient consists of a number of steps:

    Create an instance of HttpClient.
    Create an instance of one of the methods (GetMethod in this case). The URL to connect to is passed in to the the method constructor.
    Tell HttpClient to execute the method.
    Read the response.
    Release the connection.
    Deal with the response.
httpClient可以和server自动的进行验证,但是开发者需要提供给httpClient验证需要的credentials,这些信息被存储在HttpState中,httpState的instance可以通过setCredentials(AuthScope authscope, Credentials cred) and getCredentials(AuthScope authscope) 来获取和设置.在httpClient中的自动验证只可以通过httpMethod来取消,只限于这这method.setDoAuthentication(boolean doAuthentication)
当没有明确指定需要验证的时候,host会把default的验证信息发出去.所以当访问不可信的网站的时候,我们并不希望把credentials的信息发出去,所以我们在httpClient设置发送credentials的范围.以下设置的是对于任何site都发生credentials的信息,这个是不推荐的.
// To be avoided unless in debug mode
Credentials defaultcreds = new UsernamePasswordCredentials("username", "password");
client.getState().setCredentials(AuthScope.ANY, defaultcreds);
使用代理时的验证和server的验证是一样的,但是有单独的设置和获取验证的方法:
etProxyCredentials(AuthScope authscope, Credentials cred) and getProxyCredentials(AuthScope authscope).
httpClient支持的authentication scheme: basic, Digest, NTLM
1.basic的scheme是最基本的,也是兼容性最好的,但也是最不安全的,它的问题是它在传输用户名和密码的时候,没有经过加密.使用这种模式的时候,必须显示的提供或使用默认的UsernamePasswordCredentials
2.Digest很安全,它是通过client来对server发回的一个nonce的一个值进行加密,然后发回去,不会把密码发送.它的问题是,可能不是很好的被http支持.它也需要显示的提供或使用默认的UsernamePasswordCredentials
3.NTLM是由微软研发的一套验证的方法,它在windows NT4中是固定的.NTLM验证需要一个NTCredentials对于需要的domain name,或者一个default的credentials.使用NTLM与使用其他俩个验证方法有一些不同:
    a.你需要提供 NTCredentials而不是UsernamePasswordCredentials.
    b.在NTLM验证中的realm是你连的那台计算机的domain name,所以,当一台计算机的有多个domain name的时候,就会有麻烦.自有httpClient连接的那个domain name才会寻找对应的credentials.((as specified by the HostConfiguration). It is generally advised that while initially testing NTLM authentication, you pass the realm in as null which is used as the default.
    c.NTLM是一个连接而不是一个request,所以在每次开一个新的连接的时候都需要进行验证.所以,在http 1.0中不支持NTLM
我们可以提供给httpClient多种验证的scheme,但是在实际进行验证的时候,httpClient会选择其中的一个.默认三种scheme是有一定的优先级的:NTLM,Digest,Basic
The default preference of the authentication schemes may be altered using the 'http.auth.scheme-priority' parameter. The parameter value is expected to be a List of Strings containing names of authentication schemes in descending order of preference.
HttpClient client = new HttpClient();
List authPrefs = new ArrayList(2);
authPrefs.add(AuthPolicy.DIGEST);
authPrefs.add(AuthPolicy.BASIC);
// This will exclude the NTLM authentication scheme
client.getParams().setParameter(AuthPolicy.AUTH_SCHEME_PRIORITY, authPrefs);
除了以上三种http自持的验证方法,httpClient也支持customed的验证方法,只要

    1.Implement the AuthScheme interface.
    2.Register the custom AuthScheme with AuthPolicy.registerAuthScheme().
    3.Include the custom AuthScheme in the AuthPolicy.AUTH_SCHEME_PRIORITY preference (see the Alternate authentication section).
public class BasicAuthenticationExample {
46     
47     /**
48     * Constructor for BasicAuthenticatonExample.
49     */
50     public BasicAuthenticationExample() {
51     super();
52     }
53     
54     public static void main(String[] args) throws Exception {
55     HttpClient client = new HttpClient();
56     
57     // pass our credentials to HttpClient, they will only be used for
58     // authenticating to servers with realm "realm" on the host
59     // "www.verisign.com", to authenticate against
60     // an arbitrary realm or host change the appropriate argument to null.
61     client.getState().setCredentials(
62     new AuthScope("www.verisign.com", 443, "realm"),
63     new UsernamePasswordCredentials("username", "password")
64     );
65     
66     // create a GET method that reads a file over HTTPS, we're assuming
67     // that this file requires basic authentication using the realm above.
68     GetMethod get = new GetMethod("https://www.verisign.com/products/index.html");
69     
70     // Tell the GET method to automatically handle authentication. The
71     // method will use any appropriate credentials to handle basic
72     // authentication requests. Setting this value to false will cause
73     // any request for authentication to return with a status of 401.
74     // It will then be up to the client to handle the authentication.
75     get.setDoAuthentication( true );
76     
77     try {
78     // execute the GET
79     int status = client.executeMethod( get );
80     
81     // print the status and response
82     System.out.println(status + " " + get.getResponseBodyAsString());
83     
84     } finally {
85     // release any connection resources used by the method
86     get.releaseConnection();
87     }
88     }
89     }
 httpClient的编码问题主要涉及到三个方面:header,Body,还有url

headers必须是us-ascll编码.由于cookie在头里,所以cookie也必须是使用us-ascll编码.
body中的编码可以是任意的编码,默认是ISO-8859-1,它的编码可以在Content-type中指定.
Content-Type: text/html; charset=UTF-8
在这种情况下,httpClient可以放心的使用UTF-8编码来把body转换成string.你可以设置requestheader中的content-type,通过 addRequestHeader
也可以获得response body的编码方式,使用getResponseCharSet方法.如果确定返回的结果是一个string,你可以使用 getResponseBodyAsString方法,他会自动的使用content-type中指定的编码集来解出字符,如果没有content-type的header,就会使用默认的编码ISO-8859-1
需要注意的是一个文件,例如xml/html允许指定文件的编码,这种情况下,你就需要使用合适的编码来对文件进行解析,可能文件中指定的编码和response的header中不一样.
url:url的协议明确规定了url使用us-ascll编码,而且是以字节的方式来定义的.16进制的字符不在us-ascll中,所以有的十六进制是有一些控制符作为前缀的,在这些范围的字符一定要进行编码.无法使用us-ascll编码的字符无法出现在url中.
Despite this, some servers do support varying means of encoding double byte characters in URLs, the most common technique seems to be to use UTF-8 encoding and encode each octet separately even if a pair of octets represents one character. This however, is not specified by the standard and is highly prone to error, so it is recommended that URLs be restricted to the 8-bit ASCII range.
  关于redirect: 还是看原文吧: http://hc.apache.org/httpclient-3.x/redirects.html 感觉没什么重点.主要是一些重定向的status 3XX,看一下每个status表示的含义.
  使用httpClient会遇到的异常主要包括俩种异常:transport exception,还有protocol exception.不是所有的异常都会在httpClient中被抛出,有些异常在内部就会被处理掉.
  transport exception是由输入输出的失败造成的,这些错误一般是由于不可靠的网络传输,或者在一次操作中产生了超时,比如cookie的过期.一般来说,transport的异常不是致命的,可以进行重试.但是如果一个操作不是幂等的,就需要注意了.
  一般transport的exception是由ioException或者它的子类例如SocketException,InterruptedIOException来代表.除了这些标准的输入输出的异常,httpClient还自定义了一些transport exception的异常.
java.io.IOException
  +- org.apache.commons.httpclient.NoHttpResponseException
  NoHttpResponceException:这个异常是由于,当客户端向服务器扔出过多的异常,服务器可能会承担不住,所以server会接受来的请求,但是不会做出response.这个时候就会抛出这个异常.然后处理的时候,可以降低发出请求的速度,然后进行重试.
java.io.IOException
  +- java.io.InterruptedIOException
    +- org.apache.commons.httpclient.ConnectTimeoutException
   org.apache.commons.httpclient.ConnectTimeoutException:这个异常表示httpClient无法和serve或者代理建立连接,在一定的时间内,所以照成了超时.
java.io.IOException
  +- java.io.InterruptedIOException
    +- org.apache.commons.httpclient.ConnectTimeoutException
      +- org.apache.commons.httpclient.ConnectionPoolTimeoutException
    这个异常只有在使用multithreaded connection manager的时候才会发生,它表示一个connection management在一定的时间内无法在连接池中获得一个空闲的连接.
java.io.IOException
  +- org.apache.commons.httpclient.HttpException
    +- org.apache.commons.httpclient.HttpRecoverableException
         这个异常现在已经被废弃了,在标准的httpClient的类和方法中不会再被抛出.
Protocol exception.通常是由server(target server或者proxy server)和client的不匹配照成的,所以出现这个异常的时候,需要对发生的请求的一些信息进行修改.
java.io.IOException
  +- org.apache.commons.httpclient.HttpException
    这个异常表示了这种逻辑上的不匹配造成的错误.一般需要对请求信息进行修改.
java.io.IOException
  +- org.apache.commons.httpclient.HttpException
    +- org.apache.commons.httpclient.ProtocolException
    ProtocolException signals a violation of the HTTP specification. It is important to note that HTTP proxies and HTTP servers can have different level of HTTP specification compliance. It may be possible to recover from some HTTP protocol exceptions by configuring HttpClient to be more lenient about non-fatal protocol violations.
还有其他一些在内部就被处理了的异常,包括在验证操作出现的错误:验证的配置文件找不到,验证错误,验证被拒绝,cookie错误,自动处理redirect的时候出现的非法的redirect,uri不对等等异常.
  http是无状态的,所以需要注意http请求的幂等性.
  有少数的异常,httpClient在捕捉到后,会尝试进行恢复.httpClient不会对protocol的exception.当发生transport的异常的时候,httpClient会自动的重复up to 5次的操作,来尝试恢复.或者是你的request打到server后,server没有返回status,也就是说server可能把这个request丢掉了.如果在出现上述情况的时候,可能一些应用或者server的状态会发生改变,则最好我们自己去定制内部处理这些异常的方法.
  如果想要定制处理内部异常的方法,就需要实现HttpMethodRetryHandler,然后在httpMethod中setParameter,指定使用哪个RETRY_HANDLER;
HttpClient client = new HttpClient();

HttpMethodRetryHandler myretryhandler = new HttpMethodRetryHandler() {
    public boolean retryMethod(
        final HttpMethod method,
        final IOException exception,
        int executionCount) {
        if (executionCount >= 5) {
            // Do not retry if over max retry count
            return false;
        }
        if (exception instanceof NoHttpResponseException) {
            // Retry if the server dropped connection on us
            return true;
        }
        if (!method.isRequestSent()) {
            // Retry if the request has not been sent fully or
            // if it's OK to retry methods that have been sent
            return true;
        }
        // otherwise do not retry
        return false;
    }
};
        
GetMethod httpget = new GetMethod("http://www.whatever.com/");
httpget.getParams().
    setParameter(HttpMethodParams.RETRY_HANDLER, myretryhandler);
try {
    client.executeMethod(httpget);
    System.out.println(httpget.getStatusLine().toString());
} finally {
    httpget.releaseConnection();
}

   关于httpClient的log,它使用的是common log,所我们可以在底层使用其他的log实现. HttpClient performs two different kinds of logging: the standard context logging used within each class, and wire logging.
























原文地址:https://www.cnblogs.com/zhaoxinshanwei/p/5824017.html