HttpClient下完美Post/Redirect/Post
一些特殊的原因,需要利用HttpClient自动的完成Post方法的301重定向,看了默认HttpClient官方给出的默认实现是org.apache.http.impl.client.DefaultRedirectStrategy
,可以定向的方法就限定死了,
/**
* Redirectable methods.
*/
private static final String[] REDIRECT_METHODS = new String[] {
HttpGet.METHOD_NAME,
HttpHead.METHOD_NAME
};
显然是无法完成Post/Redirect/Post的,所以还得继续找Apache针对接口org.apache.http.client.RedirectStrategy
的实现,还好在IDE的帮助下很快找到了这个Apache关于自动重定向的终极实现org.apache.http.impl.client.LaxRedirectStrategy
,这回支持Post的重定向了,但是发现无法将原Post请求Body中的数据传递下去直接丢失了,这显然是非常不理想的,无法保持原有请求的完整性几乎等于白做一样。苦恼之下只能自行实现RedirectStrategy接口,不过可以继承DefaultRedirectStrategy覆盖他的isRedirected
和getRedirect
,实现关键的获取重定向后的HttpUriRequest即可,即:
@Override
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
}
看了HttpRequest 的接口后瞬间就麻木了,
public interface HttpRequest extends HttpMessage {
RequestLine getRequestLine();
ProtocolVersion getProtocolVersion();
boolean containsHeader(String name);
Header[] getHeaders(String name);
Header getFirstHeader(String name);
Header getLastHeader(String name);
Header[] getAllHeaders();
void addHeader(Header header);
void addHeader(String name, String value);
void setHeader(Header header);
void setHeader(String name, String value);
void setHeaders(Header[] headers);
void removeHeader(Header header);
void removeHeaders(String name);
HeaderIterator headerIterator();
HeaderIterator headerIterator(String name);
HttpParams getParams();
void setParams(HttpParams params);
}
我擦这TM怎么拿到原有请求数据重新封装啊?就算不给拿原有请求数据接口,给个重置URI的接口行不?查找各种官方资料,毛介绍说明都没有,啥都不行,走到这一步完全不知该如何搞了。
再次苦恼后并怀着对HttpClient团队的深深的不理解,开始尝试着利用反射机制把request和context都打印出来看看,看看这两货里面有没有我想要的东西
uir : https://t.xx.com/xxxxxx,
context : org.apache.http.client.protocol.HttpClientContext@3d04562f[context={http.cookie-origin=[t.xx.com:80/xxxxxx], http.auth.credentials-provider={}, http.connection=CPoolProxy{127.0.0.1:44087<->127.0.0.1:80}, http.response=HttpResponseProxy{HTTP/1.1 301 Moved Permanently [Server: nginx/1.7.1, Date: Thu, 30 Jul 2015 12:09:29 GMT, Content-Type: text/html, Content-Length: 184, Connection: keep-alive, Location: https://t.xx.com/xxxxxx]}, http.target_host=http://t.xx.com:80, http.protocol.redirect-locations=[https://t.xx.com/xxxxxx], http.cookie-spec=best-match, http.cookiespec-registry={ignorecookies=org.apache.http.impl.cookie.IgnoreSpecFactory@1815b417, rfc2109=org.apache.http.impl.cookie.RFC2109SpecFactory@38e25e5b, rfc2965=org.apache.http.impl.cookie.RFC2965SpecFactory@15a0c58a, compatibility=org.apache.http.impl.cookie.BrowserCompatSpecFactory@6e08f21d, standard=org.apache.http.impl.cookie.RFC2965SpecFactory@d4f865b, best-match=org.apache.http.impl.cookie.BestMatchSpecFactory@5ffdc730, netscape=org.apache.http.impl.cookie.NetscapeDraftSpecFactory@e20518c}, http.route={}->http://t.xx.com:80, http.request=POST /xxxxxx HTTP/1.1 [Content-Type: application/x-thrift, Accept: application/x-thrift, User-Agent: Java/THttpClient/HC, Content-Length: 48, Host: t.xx.com:80, Connection: Keep-Alive, Accept-Encoding: gzip,deflate], http.cookie-store=[], http.auth.proxy-scope=state:UNCHALLENGED;, http.request_sent=true, http.request-config=, expectContinueEnabled=false, proxy=null, localAddress=null, staleConnectionCheckEnabled=true, cookieSpec=null, redirectsEnabled=true, relativeRedirectsAllowed=true, maxRedirects=50, circularRedirectsAllowed=false, authenticationEnabled=true, targetPreferredAuthSchemes=null, proxyPreferredAuthSchemes=null, connectionRequestTimeout=-1, connectTimeout=10000, socketTimeout=10000], http.auth.target-scope=state:UNCHALLENGED;, http.authscheme-registry={basic=org.apache.http.impl.auth.BasicSchemeFactory@6bc9c592, digest=org.apache.http.impl.auth.DigestSchemeFactory@bca2b87, ntlm=org.apache.http.impl.auth.NTLMSchemeFactory@5efb21d, kerberos=org.apache.http.impl.auth.KerberosSchemeFactory@3b9690f6, negotiate=org.apache.http.impl.auth.SPNegoSchemeFactory@1a07250a}}],
request : org.apache.http.client.methods.HttpRequestWrapper$HttpEntityEnclosingRequestWrapper@4b10c0df[entity=org.apache.http.entity.ByteArrayEntity@1aea9900,original=POST /xxxxxx HTTP/1.1,method=POST,version=HTTP/1.1,uri=/xxxxxx,headergroup=[Content-Type: application/x-thrift, Accept: application/x-thrift, User-Agent: Java/THttpClient/HC, Content-Length: 48, Host: t.xx.com:80, Connection: Keep-Alive, Accept-Encoding: gzip,deflate],params=org.apache.http.params.BasicHttpParams@35d7c7ad]
柳暗花明又一村啊,context没啥有用的信息,但是request的实现类竟然是这货HttpRequestWrapper
,属性中竟然还有ByteArrayEntity
,这个entity应该就是我们需要的request的body数据体了,还有params
,把这些都搞出来传递下去就OK了,开心的去看HttpRequestWrapper
接口,关键数据都在封闭的内部类中完全搞不出来,那就换条思路,能修改URI不?惊喜的发现可以。果断实现一个版本,
public class HMRedirectStrategy extends DefaultRedirectStrategy {
private Logger logger = LoggerFactory.getLogger(getClass());
@Override
public boolean isRedirected(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
int responseCode = response.getStatusLine().getStatusCode();
return responseCode == 301 || responseCode == 302;
}
@Override
public HttpUriRequest getRedirect(HttpRequest request, HttpResponse response, HttpContext context) throws ProtocolException {
URI uri = getLocationURI(request, response, context);
String method = request.getRequestLine().getMethod();
if (HttpPost.METHOD_NAME.equalsIgnoreCase(method)){
try {
HttpRequestWrapper httpRequestWrapper = (HttpRequestWrapper) request;
httpRequestWrapper.setURI(uri);
httpRequestWrapper.removeHeaders("Content-Length");
return httpRequestWrapper;
}catch (Exception e){
logger.error("强转为HttpRequestWrapper出错");
}
return new HttpPost(uri);
}else {
return new HttpGet(uri);
}
}
}
测试后完美实现Post/Redirect/Post。最后吐槽下HttpClient团队,getRedirect
这个接口,几乎没有什么有用的说明,只有点象征性的注释,要不是用反射(开发测试过程中也可以用debug来看),鬼能知道HttpRequest
的实现类其实是HttpRequestWrapper
,本来这些获取基础数据和修改基础数据的接口在这里就应该开放,结果被他们藏的那么深,还搞那么复杂,然后还不给个说明。哎,也真是……(省略若干)
本站地址无极小子,转载注明出处,谢谢。